js为什么需要this?
在对象内部方法中调用对象属性更加方便
可以思考下面一种情况
var person = {
name:“person”,
say:function(){
return this.name
}
}
//上面的say方法里可以使用person.name,但是如果对象名称频繁改动,say方法里也要随之进行修改
//所以直接使用this可以利用隐式绑定(persoon.say()的形式)把this始终绑定在所处对象上
js中this到底怎么指向?
this指向和定义的位置没有关系,和调用方式及调用位置有关系
this是在运行时被绑定的,可以看做是函数调用时被绑定的一个对象
不同场景分类如下:
全局环境
浏览器环境
在window
全局作用域时,this
就指向window
Node环境
node
环境下直接在js文件打印this
,this
指向空对象{}
打印为{} 因为node中js文件是当做module模块, 然后进行解析-->加载-->编译
会将js页面所有的东西放到一个函数
运行这个函数.apply({}),利用了apply将this绑定了一个空对象
函数中的this指向(重要)
默认绑定
也可以理解为独立的函数调用
-
普通函数调用
function foo1(){ console.log(this); //window } function foo2(){ console.log(this); //window foo1() } foo2()
-
函数赋值给另一个变量再调用(也可以看做是隐式绑定丢失)
const obj1 = { name:'obj1', foo:function(){ console.log(this); } } const bar = obj1.foo bar() //window
-
函数调用链
function foo(){ function bar(){ console.log(this); } return bar } const fn = foo() fn() //window
隐式绑定
必须在调用的对象内部有一个对函数的引用, 是通过某个对象发起的函数调用
-
通过对象直接调用函数
const obj = { name:'obj', eating:function(){ console.log(this.name + ' is eating'); } } obj.eating() //'obj is eating'
-
将对象1属性对应的函数对象赋值给对象2中的某个属性
const obj1 = { name:'obj1', foo:function(){ console.log(this); } } const obj2 = { name:'obj2', bar:obj1.foo } obj2.bar() // {name: 'obj2', bar: ƒ}
显式绑定
当不希望对象内部包含某个函数的引用, 又希望在对象上进行强制调用
比如apply/bind/call
函数, 可以显式绑定this
到上述对象
关于apply/bind/call
函数的手动实现, 可以查看我的js专栏
相关文章
-
call/apply
函数绑定function foo(num1,num2){ const num = num1+num2 console.log('num:',num,this); } foo.call('call',1,2,3) // 3, 'call' foo.apply('apply',[2,3,4]) //5, 'apply'
-
bind
函数绑定function foo(){ console.log('foo:',this); } const newFoo = foo.bind('newFoo') //bind完之后生成一个新的函数 newFoo() // 'foo:' newFoo //注意此时,既是显式绑定又是默认绑定,优先级: 显式绑定>默认绑定
new关键字绑定
js函数可以用new
关键字被当做一个类的构造函数使用
new
关键字调用函数(构造器),this
是在调用构造器时创建出来的对象
-
构造函数直接用new调用
function Person(name,age){ this.name = name this.age = age } const p1 = new Person('p1',100) console.log(p1.name,p1.age); // p1 100 const p2 = new Person('p2',200) console.log(p2.name,p2.age); // p2 200
-
构造函数和显式绑定冲突的情况
const obj = { foo:function(){ return this } } console.log(new obj.foo()); //foo对象 //可以看出 new关键字>显式绑定
综合上述例子, 可以发现this
绑定优先级:
new关键字 > 显式绑定 > 隐式绑定 > 默认绑定
this绑定的特殊情形
setTimeout
setTimeout内部通过apply进行绑定this到全局对象
所以this指向window
setTimeout(function(){
console.log('setTimeout',this); //window
},0)
监听点击事件
this指向点击的dom对象
const boxDiv = document.querySelector('.box')
boxDiv.addEventListener('click',function(){
console.log(this); //box实例
})
forEach/map方法
默认在传入的函数中打印的也是Window对象
因为在默认情况是自动调用函数(默认绑定)
但是可以传入第二个参数进行this绑定
const names = ['a','b','c']
names.forEach(function(item){
console.log(item,this); //'thisArg'
},'thisArg')
间接引用
const obj1 = {
name:'obj1',
foo:function(){
console.log(this);
}
}
const obj2 = {
name:'obj2',
};
obj2.foo = obj1.foo
obj2.foo() //{name: 'obj2', foo: ƒ}
立即引用
这种调用方式注意分号;
的使用
const obj1 = {
name:'obj1',
foo:function(){
console.log(this);
}
}
const obj2 = {
name:'obj2',
};
(obj2.bar = obj1.foo)() //window
//独立调用的一种:可以看做直接拿到了foo函数进行调用
箭头函数
在函数声明时就绑定好了this, 可以看做是一个闭包
且箭头函数不支持new运算符
const name = 'name'
const foo = ()=>{
console.log(this);
}
foo()
const obj = {foo:foo} //window
obj.foo() //window
foo.call('call') //window
根据外层作用域决定this 会往上一级寻找
const obj = {
data:'',
getData:function(){
//在此处不需要定义_this=this
setTimeout(()=>{
console.log(this);
},1000)
}
}
obj.getData() // {data: '', getData: ƒ}