JavaScript基础(三)
一、this的绑定问题
this是当前执行环境的一个属性,除了全局环境的this绑定的是全局对象之外,this都是运行时绑定,所以函数环境中的this指向取决于函数的调用(箭头函数例外,本文也会详细说),并且严格模式和非严格模式也会有一些不同。
下面举例介绍this的五种绑定。
1)默认绑定
当函数独立调用时,非严格模式下,this默认绑定全局对象(浏览器中就是window);严格模式下,this为undefined。
// 1、直接调用
function foo() {
console.log(this)
}
foo()
// 2、对象中的函数被变量引用
var obj1 = {
name: 'obj1',
foo: foo
}
var fn1 = obj1.foo //请思考:直接以obj1.foo()的方式调用会是同样的结果吗?
fn1()
// 3、函数嵌套调用
function foo1() {
console.log('foo1', this)
}
function foo2() {
console.log('foo2', this)
foo1()
}
function foo3() {
console.log('foo3', this)
foo2()
}
foo3()
// 4、通过闭包调用
var obj2 = {
name: 'obj2',
bar: function () {
name: 'bar'
return function () {
console.log(this)
}
}
}
obj2.bar()()
! 运行结果
这几种调用下,this都是默认绑定。
2)隐式绑定
调用函数的对象内部有对函数的引用,接着上边的例子看:
obj1.foo()
var obj3 = {
name: 'obj3',
bar: function () {
console.log(this)
}
}
obj3.bar()
var obj4 = {
name: 'obj4',
baz: obj3.bar
}
obj4.baz()
! 运行结果
通过对比代码和结果,可以发现:函数是由哪个对象调用的,其this绑定的就是哪个对象。以上这三种调用方式都属于隐式绑定。
3)显式绑定
对象内部没有这个函数的引用,但又希望通过对象强制调用(把this的值从一个运行环境传到另一个),使用call / apply / bind 进行显式绑定。
var obj5 = {
name: 'obj5',
}
foo.call(obj5)
foo.apply(obj5)
foo.call("xxx")
! 运行结果
foo默认this是绑定window的,这里通过call和apply来改变了this的绑定。bind方法改变绑定的效果是一样的,只不过bind返回的不是函数运行结果,而是改变后的函数(具体请单独查询bind的使用)。
4)new绑定
通过new关键字来创建构造函数的实例,绑定this。MDN中将这部分单独分在了this在类中的绑定,其实本质上一样,类就是通过构造函数实现的。
function Person(name, age) {
this.name = name
this.age = age
}
const p1 = new Person('mmm', 18)
const p2 = new Person('ppp', 25)
console.log(p1)
console.log(p2)
! 运行结果
此时this指向的是通过new创建的实例对象。
5)几种特殊绑定
- 箭头函数
在箭头函数中,this与封闭词法环境的this保持一致;在全局代码中,它将被设置为全局对象。说白了就是,箭头函数的this绑定的是上级作用域,找外层。 - 忽略显示绑定
当显示绑定的值为 null/undefined 时,this直接绑定全局对象。 - 间接函数引用
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this)
}
}
var obj2 = {
name: 'obj2'
};
obj2.baz = obj1.foo;
obj2.baz();
(obj2.bar = obj1.foo)();
! 运行结果
两种方式所绑定的this不同,第二种方式进行了赋值调用,实际上是间接函数引用。(obj2.bar = obj1.foo)这里返回了赋值的结果,再加上一个小括号,就直接调用赋值的结果函数。
二、this绑定的优先级
先说结果,一中除特殊绑定之外的四种绑定优先级顺序为:new关键字 > 显式绑定 > 隐式绑定 > 默认绑定。
//隐式绑定优先级高于默认绑定
var name = 'global'
function foo() {
console.log(this.name)
}
var obj = {
name: 'obj',
foo: foo
}
obj.foo() //:obj
//显示绑定优先级高于隐式绑定
var obj1 = {
name: 'obj1',
foo: foo.bind('obj2')
}
obj1.foo() //:obj2
//new关键字优先级高于显式绑定
function bar(){
console.log(this.name)
}
var obj3 = {
name: 'obj3'
}
var fn = bar.bind(obj3)
fn() //:obj3
new fn() //:bar {}
最后的例子,当同时存在于new关键字绑定和显示绑定时,this绑定了foo构造函数,所以new关键字的优先级高于显示绑定。
参考文章:彻底弄懂js中this指向