JavaScript this 绑定详解
函数内 this 绑定函数内this的绑定和函数定义的位置没有关系,和调用的方式和调用位置有关系,函数内的this是在被调用执行时被绑定的。
this的具体绑定规则
this 绑定基本包含下面4种绑定规则,以及一些其它的特殊绑定规则:
[*]默认绑定
[*]隐式绑定
[*]显式绑定
[*]new绑定
默认绑定
独立的函数被调用,那么里面的this绑定的是window,也就是调用时没有被绑定到具体的对象。
比如普通的函数调用:
function foo() {
console.log(this); // window
}
foo();第二种情况是函数调用链:
function test1() {
console.log(this); // window
test2();
}
function test2() {
console.log(this); // window
test3();
}
function test3() {
console.log(this); // window
}
test1();在调用每一个方法的时候,方法的前面没有绑定对象,并不是像obj.test1()这种形式前面绑定了对象。
第三种情况是函数作为参数:
function foo(func) {
func();
}
function bar() {
console.log(this); // window
}
foo(bar);同上,虽然看起来bar到了foo的上下文中了,但是调用bar函数时,前面没有绑定对象,那么还是默认绑定,是由window调用的。
可以再看一下上例中的变例:
function foo(func) {
func();
}
var obj = {
name: "easylee",
bar: function () {
console.log(this);// window
},
};
foo(obj.bar);上面的变例,表面上看,调用的是obj对象的bar方法,但是具体执行的时候,是将这个方法整体作为参数传入foo,foo里面执行func()的时候,执行func()并没有绑定任何对象,调用obj.bar传入参数的时候,只是调用并没有执行,所以还是window。
一定要注意,只有在调用执行的时候绑定了对象才算绑定,定义的时候就算它绑定了,但是调用并执行的时候未绑定就不算绑定。
提示:
在严格模式下,默认绑定模式,直接调用this(也就是window)会打印undefined,在webpack等打包工具中,通常会使用严格模式,来避免一些低级的语法错误。
所以在直接打印this的地方,直接使用window来代替 this
隐式绑定
通过一个对象,调用对象里面的函数,也就是通过某个对象发起的函数调用,此时函数的this就会绑定为这个对象。
function foo() {
console.log(this);
}
let obj1 = {
name: "obj1",
foo: foo
};
let obj2 = {
name: 'obj2',
obj1: obj1
}
obj1.foo();// obj1
obj2.obj1.foo();// obj1
let bar = obj1.foo;
bar();// window,还是默认绑定,最终还是window在调用这个函数隐式绑定有一个前提是调用的对象内部一定有一个对函数的引用,如上例中的foo:foo,对象一定有一个引用函数的属性,通过这个引用,间接的将函数里面的this绑定到这个对象中。
显式绑定
如果不希望在对象内部包含函数引用的属性,同时又希望this绑定到这个对象上,那么可以使用显示绑定,就不需要在这个对象设置函数引用属性了。
通常使用 call 、apply和 bind 来给this显式的绑定一个对象。
使用call、apply均是显示的指定函数执行时,函数中this的指向,通过第一个参数传递需要绑定的对象。
三者的区别,可以看:https://www.cnblogs.com/easy1996/p/17954254
let name = "window";
function foo() {
console.log(this.name);
}
let obj1 = {
name: "obj1",
};
foo();// window
foo.call(obj1); // obj1
foo.apply(obj1);// obj1可以看到,后面两个函数显式指定了对象,其中的this打印的是 obj1 的 name 属性。
bind函数也是一样的,只是还需要调用一遍:
function foo() {
console.log(this.name);
}
let obj1 = {
name: "obj1",
};
let bar = foo.bind(obj1);
bar(); // obj1临时绑定对象一般使用call和apply即可,像是很多框架中,使用bind来绑定并创建新的函数,之后均使用新函数即可,这样做的好处是,可以固定的绑定一个对象,因为新函数是绑定后创建的,那么里面的this则不会再发生改变。
new绑定
new绑定则非常简单,类里面的 this 就是 new Class() 创建对象时,创建后的对象。
function Person() {
console.log(this);// {name: "person1"} 和 {name: "person2"}
}
let p = new Person();
p.name = 'person1'
let p2 = new Person();
p2.name = 'person2'其它绑定规则
有一些绑定规则不遵循上面的四条绑定规则,同时也是非常常用的规则:
1.显式绑定忽略
如果call、apply、bind绑定的对象是null或者undefined那么显示绑定会被忽略,转变为默认绑定,this 指向 window。
2.自执行间接函数引用
如果使用自执行的方式去执行,引用其它对象的函数的这种形式,那么会指向window。具体效果查看代码,这个记住就行,不要去套上面的规则,肯定套不上:
let obj1 = {
name: 'obj1'
};
let obj2 = {
name: 'obj2',
foo: function () {
console.log(this.name);
},
};
obj2.foo(); // obj2
obj1.bar = obj2.foo;
obj1.bar();// obj1
(obj1.foo = obj2.foo)();// window不是obj1,是window调用的3.内置函数的绑定
setTimeout内绑定的是window:
setTimeout(function () {
console.log(this); // window
});forEach等遍历函数的绑定:
["foo", "bar"].forEach(function () {
console.log(this); // window 默认是window
});
let obj1 = {
name: "obj1",
};
["foo", "bar"].forEach(function () {
console.log(this); // obj1,可以传递绑定的对象参数
}, obj1);元素的事件默认绑定的是元素对象,除非使用箭头函数:
const box = document.querySelector(".box");
box.onclick = () => {
console.log(this);// window
};
box.onclick = function() {
console.log(this);// box对象
}4.箭头函数的绑定
箭头函数的this不绑定任何对象,而是从上层作用域中找到对应的this的绑定对象。
let obj1 = {
name: "obj1",
foo: () => {
console.log(this);
},
bar: function () {
console.log(this);
},
};
obj1.foo(); // window 当前默认是obj1对象中,使用箭头函数所以上一层是window
obj1.bar(); // obj1要注意箭头函数不仅默认this不绑定对象,而且是无法绑定,就算通过显式绑定,也无法生效。
常见题目
常见题目1
let name = "window";
let person = {
name: "person",
sayName: function () {
console.log(this.name);
},
};
function sayName() {
let sss = person.sayName;
sss();// window
person.sayName(); // person
(person.sayName)(); // person 这里就是单纯的执行一下,和上面没区别
(b = person.sayName)(); // window
}
sayName();常见题目2
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
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常见题目3
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
person1.foo2.call(person2); // 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常见题目4
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
person1.obj.foo2.call(person2)(); // person2
person1.obj.foo2().call(person2); // obj
来源:https://www.cnblogs.com/easy1996/p/17954257
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页:
[1]