this 的绑定规则,决定了 this 指向的是什么
独立函数调用, 使用的是 默认绑定
独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用
// 默认绑定 : 独立函数调用
// 案例1
function foo(params) {
console.log(this);
}
foo()
// 案例2
function foo1(params) {
console.log(this);
}
function foo2(params) {
console.log(this);
foo1()
}
function foo3(params) {
console.log(this);
foo2()
}
foo3()
// 案例 3
var obj = {
name: "ddg",
foo: function () {
console.log(this);
}
}
var bar = obj.foo
bar() // 此时的 bar 调用的时候 没有主题,所以是 window
console.log('案例四~~~~~~~~~~~~~~~');
// 案例4
function foo4(params) {
console.log(this);
}
var anli4 = {
name: "呆呆狗",
foo: foo4
}
var bar4 = anli4.foo
bar4() // bar4 在调用的时候 还是 独立函数调用 所以还是 window
// 案例5
console.log('案例 ~~~~~~~~');
function foo5() {
return function () {
console.log(this);
}
}
var fn5 = foo5()
fn5() // fn5 在调用的时候 还是 独立调用 所以还是 window
隐式绑定比较常见的就是 通过 某个对象进行调用的
就是它的调用位置中,是通过某个对象发起的函数调用
// 案例 1
// function foo() {
// console.log(this);
// }
// var obj = {
// name: "ddg",
// foo: foo
// }
// obj.foo()
// 案例 2
// var obj = {
// name: "ddg",
// eating: function () {
// console.log(this.name + ' 在吃饭');
// },
// running: function () {
// console.log(this.name + ' 在跑步');
// },
// }
// obj.eating()
// obj.running()
// 案例3
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
}
}
var obj2 = {
name: "obj2",
bar: obj1.foo
}
obj2.bar() // 此时的 this 指向的是 obj2, 调用主题是 obj2
var fn = obj2.bar
fn() // window
call 和 apply 在执行函数的时候,是可以明确绑定函数this ,这种被称为显示绑定
函数的实现 会有很多的 edge case (边界判断) , 就是 特殊的情况
// 给所有函数添加 _call 方法
Function.prototype._call = function (thisArg, ...restParameters) {
// 执行 调用的那个函数 , 其实是一个隐式绑定
// 如果是一个 单纯的 数字, Object(数字) 可以转换成 对象模式
// Object(null/undefined) 会返回一个空对象
// 1. 获取需要被执行的函数
var fn = this
// 2.对 thisArg 转换成对象类型 ( 防止它传入的是非对象类型 )
thisArg = thisArg ? Object(thisArg) : window
// 3.调用需要被执行的函数
thisArg.fn = fn
var result = thisArg.fn(...restParameters)
delete thisArg.fn
// 4.将 结果返回
return result
}
function foo(num1, num2) {
console.log('foo 被执行', this, num1, num2);
return num1 + num2
}
var result1 = foo._call({ name: "呆呆狗" }, 100, 200)
var result2 = foo._call('呆呆狗')
var result13 = foo._call(null)
console.log('返回结果:', result1);
Function.prototype._apply = function (thisArg, argArray) {
// 当传递一个参数的时候, 会报错,argArray 是 没有值的,此时的 argArray 就是 undefined
// 解决方案:1. 可以判断 argArray 有没有值
// 1. 拿到 函数
var fn = this
// 2. 转换成 对象 (如果 thisArg 是 0 或者 '' 呢? Boolean(0或者空字符串) 的结果是 false)
thisArg = thisArg ? Object(thisArg) : window
// 3. 执行
thisArg.fn = fn
// var result = thisArg.fn(...argArray)
// 解决 argArray 不传递的情况 , 方案 1
// var result
// if (!argArray) {
// // argArray 没有传递参数的时候
// result = thisArg.fn()
// } else {
// result = thisArg.fn(...argArray)
// }
// 解决 argArray 不传递的情况 , 方案 2
argArray = argArray ? argArray : []
var result = thisArg.fn(...argArray)
delete thisArg.fn
return result
}
function sum(num1, num2) {
console.log('sum 被调用');
return num1 + num2
}
// 如果是一个参数呢
function sum1(params) {
return params
}
// 如果没有传递参数 ,
function sum2() {
}
var result = sum._apply({ name: "呆呆狗" }, [10, 20])
var result1 = sum1._apply({ name: "呆呆狗" }, [10])
var result2 = sum2._apply({ name: "呆呆狗" },)
console.log('结果1', result);
console.log('结果2', result1);
console.log('结果3', result2);
Function.prototype._bind = function (thisArg, ...argArray) {
// 1. 获取到真实调用的函数
var fn = this
// 2.绑定this
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
function proxyFn(...args) {
// 3.
var finalArgs = [...argArray, ...args]
thisArg.fn = fn
var result = thisArg.fn(...finalArgs)
delete thisArg.fn
return result
}
return proxyFn
}
function sum(num1, num2, num3, num4) {
console.log('sum被执行', this);
console.log('参数', num1, num2, num3, num4);
return 20
}
var bar = sum._bind({ name: "呆呆狗" }, 10, 20)
var result = bar(30, 40)
console.log(result);
new 函数关键词来调用函数,会执行如下的操作
new 绑定 > 显示绑定(apply/call/bind) > 隐式绑定 (obj.foo() ) > 默认绑定 ( 独立函数调用 )
// var obj = {
// name: "ddg",
// foo: function () {
// console.log(this);
// }
// }
// obj.foo()
// obj.foo.call(window) // 此时的 this 是 window
// obj.foo.call("abc") // 此时的 this 是 “abc”
// 所以 call apply 显示绑定 高于 隐式绑定
// 2 bind 隐式绑定
// var bar = obj.foo.bind("bind")
// bar()
// 3. 更明显的比较
function foo() {
console.log(this);
}
var obj = {
name: "obj",
foo: foo.bind("aaa")
}
obj.foo()
// bind 的显示绑定 也高于 隐式绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
}
}
// new 的优先级 高于 隐式绑定
var f = new obj.foo() // 此时的this 是 foo
// 结论 new 关键字 不能 和 apply/call 一起来使用
// new bind
function foo() {
console.log(this);
}
var bar = foo.bind("aaa")
var obj = new bar() // foo
function foo() {
console.log(this);
}
foo.apply("abc")
foo.call({})
// 当我们传入 null 或者 undefined ,函数内部会把this 绑定为 window
foo.call(null) // window
foo.call(undefined)// window
var bar = foo.bind(null)
bar() // window
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
}
}
var obj2 = {
name: "obj2"
};
// 如果 第九行,不加分号,那么 执行会报错
// 如果不加, 下面用小括号 括起来的 代码 会看成 和 上面的 obj2 大括号是一个整体
// obj2.bar = obj1.foo()
// obj2.bar() // obj2
(obj2.bar = obj1.foo)() // window , 这里会作为一个独立函数调用
箭头函数 不会绑定 this、argument 属性
箭头函数不能作为构造函数来使用 ( 不能和 new 一起来使用,会抛出错误 )
箭头函数的 this 是根据外层作用域来决定 this
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window: 独立函数调用
person.sayName(); // person: 隐式调用
(person.sayName)(); // person: 隐式调用 。 这里的小括号 加不加没有影响
(b = person.sayName)(); // window: 赋值表达式(独立函数调用) 。 因为这里有一个赋值,然后再调用
}
sayName();
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}
var person2 = { name: 'person2' }
// 定义 对象 不产生作用域
// person1.foo1(); // person1(隐式绑定)
// person1.foo1.call(person2); // person2(显示绑定优先级大于隐式绑定)
// person1.foo2(); // window(不绑定作用域,上层作用域是全局)
// person1.foo2.call(person2); // window , 箭头函数不绑定 this
// person1.foo3()(); // window(独立函数调用)
// foo3调用的时候,绑定的是 person1 , 而 foo3 返回的函数,调用的时候没有 主题,是一个独立函数调用
// person1.foo3.call(person2)(); // window(独立函数调用)
// person1.foo3().call(person2); // person2(最终调用返回函数式, 使用的是显示绑定)
// person1.foo4()(); // person1(箭头函数不绑定this, 上层作用域this是person1)
// person1.foo4.call(person2)(); // person2(上层作用域被显示的绑定了一个person2)
// person1.foo4().call(person2); // person1(上层找到person1)
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // person1
person1.foo1.call(person2) // person2(显示高于隐式绑定)
person1.foo2() // person1 (上层作用域中的this是person1)
person1.foo2.call(person2) // person1 (上层作用域中的this是person1)
person1.foo3()() // window(独立函数调用)
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
var obj = {
name: "obj",
foo: function() {
}
}
var name = 'window'
function Person(name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj
// foo2 是被 obj 调用的,所以 foo2的this 是 obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj
// 上层作用域的理解
// var obj = {
// name: "obj",
// foo: function() {
// // 上层作用域是全局
// }
// }
// function Student() {
// this.foo = function() {
// }
// }