💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
### 绑定规则 #### 默认绑定 ``` function foo(){console.log(this.a)} var a= 2; foo(); // 2 ``` 函数foo是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定。在严格模式下默认绑定将会失效,this.a将会变成undefined。 #### 隐式绑定 调用的位置有上下文对象,将会使用隐式绑定: ``` var obj = {a: 3, foo: foo} obj.foo(); //3 ``` foo在被调用时是被obj包含的,this自动指向了该上下文对象。 不正确使用隐式绑定将会导致应用默认绑定: ``` var a = 2; var obj = {a:3, foo: foo} var foo1 = obj.foo; foo1(); //2 //foo在被调用时已经是位于window位置了,obj已经不是它的上下文了。 ``` 举个更常见、更微妙、更易出错的例子: ``` var a = 2; function doFoo(fn){ fn() } var obj = {a: 3, foo: foo } doFoo(obj.foo); //2 //相当于 doFoo(obj.foo){ var fn = obj.foo; fn(); } //或者你这么理解:将obj.foo赋给fn形参是在doFoo中进行的,doFoo在哪?window,所以使用了默认绑定 //setTimeout(obj.foo, 100)结果也是2,原理同上,将obj.foo赋给回调是在timeout中进行的,setTimeout位于window ``` #### 显式绑定call\apply\bind ``` var a = 2; function foo(){console.log(this.a)} var obj = {a: 3}; foo.call(obj); //3 ``` #### new绑定 与其他语言不同,使用new操作符调用的函数并不属于任何类,也不会实例化一个类,它仅仅是被new操作符调用的普通函数而已。 使用new来调用函数,会执行下面操作: 1. 创建一个新对象 2. 新对象执行[[Proptptype]],新对象的原型指向该函数的原型 3. 新对象绑定到函数调用的this 4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个对象 ``` function foo(a){this.a = a}; var bar = new foo(2); console.log(bar.a); //2 //1. 创建一个{} //2. {}.__proto__指向foo.prototype //3. foo.call({}) //4. 由于foo没有返回其他对象,因此new foo将会自动将{}返回,由于foo已经执行了,此时this.a = 2 ``` new操作符的关键就在于无论之前以何种方式绑定,使用new后this都会指向新创建的{}上。 #### 优先级 1. 函数是否在new中调用(new绑定)?是的话this(以下均指函数内部的this)指向新创建的对象。`var bar = new foo()` 2. 函数是否通过call、apply、bind绑定?是的话this指向绑定的对象。`var bar = foo.call(obj1)`。注意,call和apply均直接运行函数,而bind将绑定后的函数返回。 3. 函数是否在某个上下文对象中调用?是的话this指向那个上下文对象`var bar = obj1.foo()` 4. 如果以上都不是的话,使用默认绑定,在严格模式下就会绑定到undefined上,否则绑定到全局对象上。 #### 绑定例外 1. 把null、undefined作为this传入到call、apply或bind中,this会使用默认绑定。 ``` function foo(){console.log(this.a)} var a = 2; foo.call(null); //2 ``` 为了方便将参数传递给下层函数,使用柯里化是一个很好的选择,将this绑定到window上 ``` function foo(a, b){console.log("a:" + a + ",b:" + b)} foo.apply(null, [2, 3]); // a:2,b:3 //使用柯里化 //bar相当于function foo(b){...}, a已经变成"a"了 var bar = foo.bind(null, 2); bar(3); //a:2,b:3 //坏处就是this指向了window,多添加了a和b两个全局变量 ``` 传入null或undefined后会导致this指向window,更优雅的写法是传入一个特殊对象,将this指向它。使用Object.create(null)来创建它。 ``` var $ = Object.create(null); foo.apply($, [2, 3]); //a:2,b:3 var bar = foo.bind($, 2); bar(3); //a:2,b:3 ``` 2.间接引用 ``` function foo(){console.log(this.a)} var a = 2; var o = {a: 3, foo: foo}; var p = {a: 4} o.foo(); //3 (p.foo = o.foo)(); //2 //赋值表达式的返回值是目标函数的引用,也就是立即执行函数,因此调用位置是foo()而不是p或o。 ``` #### 箭头函数 箭头函数不使用四种绑定规则中的任意一种,this的指向完全取决于外层作用域,且一旦绑定,再也无法修改。 ``` function foo() { return (a) => console.log(this.a); } var obj1 = { a: 2 } var obj2 = { a: 3 } var bar = foo.call(obj1); bar.call(obj2); //2,而不是3 ```