js前端this指向规则
一言以蔽之:
- this只和调用方式(调用者)有关
- this是在运行时被绑定的
直接调用
当函数直接调用时,this指向的是window
function foo() {console.log(this)}foo()
打印结果:
[object Window]
隐式绑定(对象)
当函数是以对象调用时,打印的是调用的对象
function foo() {console.log(this)}var obj1 = {name: "obj1",foo: foo}obj1.foo()
打印结果:
{
“name”: “obj1”
}
显式绑定(call/apply/bind)
分为call/apply和bind
function foo(name,age) {console.log(this,name,age)}var obj1 = {name: "obj1",foo: foo}foo.call(obj1,'zhangsan',18)foo.apply(obj1,['zhangsan',18])var bar = foo.bind(obj1,'zhangsan',18)bar()
执行结果:
new关键字绑定this
首先要清楚,new调用流程:(通过new关键字创建一个新对象的步骤是什么/构造函数时如何创建新对象的)
- 创建一个新的对象
- 空的对象的__proto__属性指向构造函数的Prototype属性(原型链)
- 执行构造函数,如果构造函数中有this,则将this指向这个对象
- 返回创建的对象
function foo(name) {console.log(this) // foo{}this.name = name // foo{name:'obj1'}}var obj1 = new foo('obj1')
优先级
new>显式>隐式
经典面试题分析1
// 错误的题目function foo(){console.log(this)}var obj1 = {name:'obj1',foo: foo}var obj2={name:'obj2'}// 错误原因在这里没有分号// 由js的自动分号插入(ASI)导致// 参考:https://juejin.cn/post/7325243117861519370(obj2.foo = obj1.foo)()
报错:
index.html:21 Uncaught TypeError: Cannot set properties of undefined (setting ‘foo’)
at index.html:21:19
(anonymous) @ index.html:21
正确的题目
function foo(){console.log(this)}var obj1 = {name:'obj1',foo: foo}var obj2={name:'obj2'}; // 这里有分号(obj2.foo = obj1.foo)()
结果:
window
这里的(obj2.foo = obj1.foo)()等价于foo(),显然属于直接调用,打印window
参考:https://juejin.cn/post/7325243117861519370
经典面试题分析2
var name = '全局window'var person = {name: 'person',sayName: function(){console.log(this.name)}}function sayName(){var fun = person.sayNamefun() // windowperson.sayName();(b = person.sayName)()}sayName()
结果:
这里还有个注意点,当我们不声明name这个变量,window.name这个也是存在的,默认是一个空字符串。
并且,如果你①先运行上面这段代码,②再把var name = '全局window’这句注释,③刷新页面,会发现还是显示全局window而不是空字符串!这里的原理是:
window.name是一个所有浏览器都有的属性,表示浏览器窗口的名称,默认是一个空字符串,所有浏览器都是个空字符串。
window.name有个很有意思的跨页面特性,具体描述为:页面如果设置了window.name,即使进行了页面跳转到了其他页面,这个window.name还是会保留,刷新也是。
可以参考:https://www.zhangxinxu.com/wordpress/2019/09/window-name/