一言以蔽之:
- this只和调用方式(调用者)有关
- this是在运行时被绑定的
直接调用
当函数直接调用时,this指向的是window
js
function foo() {
console.log(this)
}
foo()
打印结果:
object Window
隐式绑定(对象)
当函数是以对象调用时,打印的是调用的对象
js
function foo() {
console.log(this)
}
var obj1 = {
name: "obj1",
foo: foo
}
obj1.foo()
打印结果:
{
"name": "obj1"
}
显式绑定(call/apply/bind)
分为call/apply和bind
js
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指向这个对象
- 返回创建的对象
js
function foo(name) {
console.log(this) // foo{}
this.name = name // foo{name:'obj1'}
}
var obj1 = new foo('obj1')
优先级
new>显式>隐式
经典面试题分析1
js
// 错误的题目
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
js
var name = '全局window'
var person = {
name: 'person',
sayName: function(){
console.log(this.name)
}
}
function sayName(){
var fun = person.sayName
fun() // window
person.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/