1.函数的this指向是谁调用就指向谁
全局下的函数(window)
function test(){
var s = 's'
console.log(this.s)
}
test() // undefined
因为此时 test()等价于 window.test()
所以this指向了window window.s自然是undefined
对象的函数
var o = {
test: function() { console.log(this.s)
},
s: 's'
}
o.test() // s
此时this指向了o
对象的对象的函数
var o = {
s: 's',
oo: {
s: 'ss',
test: function() {console.log(this.s)}
}
}
o.oo.test() // ss
此时最终是oo调用了test所以this指向oo
特殊的用法
var o = {
s: 's',
oo: {
s: 'ss',
test: function() {console.log(this.s)}
}
}
var test = o.oo.test
test() // undefined
因为此时就是window在调用test
构造函数的this
function F() {
this.name = 's'
this.test = function() {console.log(this.name)}
}
var f = new F()
f.test() // s
因为F里面的this指向了f
参考前面写过的new操作符
帮你回忆下
封装一个mynew
function mynew(fn) {
var o = {}
o.__proto__ = F.prototype
var res = fn.call(o)
return res instanceof Object ? res : o
}
上面用了call改变this指向
再回忆下 封装一个mycall
function mycall(o) {
if(typeof this != "function") return new TypeError("is not a function")
o.fn = this
o.fn()
o.fn = null
}
Function.prototype.mycall = mycall
测试下
var o = { name: 'ss' }
function _console() {
console.log(this.name)
}
_console.mycall(o) // ss
看到了别人总结了这个
function Fn()
{
this.user = '追梦子';
return {};
}
var a = new Fn;
console.log(a.user); //undefined
事实上,这可以用上面new的过程解释,而不必归于特殊情况
匿名函数的this
(function() {console.log(this)})() // window
进一步验证
function test() {
(function() {console.log(this)})()
}
test()
-------------------------
function test1() {
function test() {
(function() {console.log(this)})()
}
test()
}
test1()
可见匿名函数里面的this一直指向window
setTimeout()回调函数的this
setTimeout(function() {console.log(this)}, 1000) // window
---------------------------------
setTimeout(() => {console.log(this)}, 1000) // window
---------------------------------
function test() {
setTimeout(function() {console.log(this)}, 1000)
}
test() // window
----------------------------------
function test() {
setTimeout(() => {console.log(this)}, 1000)
}
test() // window
上面的各种情况都是window
setTimeout()是一个全局函数 是window的一个函数
var o = {
s: 's',
test: function() {setTimeout(function() {console.log(this.s)}, 1000)}
}
o.test() // window
--------------------------
var o = {
s: 's',
test: () => {setTimeout(function() {console.log(this.s)}, 1000)}
}
o.test() // undefined
这说明箭头函数里面的setTimeout的回调指向的不是window
var o = {
s: 's',
test: () => {setTimeout(() =>{console.log(this.s)}, 1000)}
}
o.test() // undefined
--------------------------------
var o = {
s: 's',
test: function() {setTimeout(() =>{console.log(this.s)}, 1000)}
}
o.test() // s
箭头函数的this
var o = {
s: 's',
test: () => {console.log(this)}
}
o.test() // window
如果是普通函数,那么this指向的就是调用者o
但这里实际指向了window
这里还有疑问下面补充
箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
作用域链其实就是一个执行栈
当进入一个函数时,创建阶段,生成this,这个就是一个作用域,压入执行栈。
如上面的例子,在o.test()压栈时,在它之下就是全局作用域。为了验证这个观点,先在全局作用域上面压一个作用域
function Person() {
// Person() 构造函数定义 `this`作为它自己的实例.
this.age = 0;
setInterval(function growUp() {
// 在非严格模式, growUp()函数定义 `this`作为全局对象,
// 与在 Person()构造函数中定义的 `this`并不相同.
this.age++;
}, 1000);
}
var p = new Person();
这里setInterval交给JS引擎之外的webcore的timer模块运作,时间一到,注册宏任务,事件循环检查时压入执行栈,创建自己的执行上下文(创建变量对象:首先初始化函数的参数arguments,提升函数声明和变量声明,创建作用域链,确定this指向),执行。所以造成了this不同
----------------
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// 回调引用的是`that`变量, 其值是预期的对象.
that.age++;
}, 1000);
}
这里的解决方法是将this保存在一个that变量中,当回调函数压栈时,沿着作用域链寻找到了that
---------------------
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确地指向 p 实例
}, 1000);
}
var p = new Person();
这里使用了箭头函数解决。箭头函数作为回调,当回调入栈时,不会创建this,而是沿着自己的作用域链寻找最近的this
回到上面的疑问
var o = {
s: 's',
test: () => {console.log(this)}
}
o.test() // window
为什么是window
因此此时该函数上下文的下面就是全局上下文
于是我想验证这个观点,就需在这个箭头函数压栈前先压一个我想压的栈
箭头函数不会提升
function a() {
let b = 111
var test = () => {console.log(this)}
test()
console.log('haha')
}
var oo = new a() // a
因为在全局上下文先压入了a上下文,他有自己的this,当再调用箭头函数test时,test的this会找到最近的this也就是a
// 普通函数的调用不产生自己的this