[js] this (2)
call、apply 、函数执行的本质
严格模式下,fn 里的 this 就是 call 的第一个参数,也就是 undefined。
"use strict"
function fn(a, b) {
console.log(this)
}
fn(1, 2)//undefined
//等价于
fn.call(undefined, 1, 2)//undefined
fn.apply(undefined, [1, 2])//undefined
//window
window.fn(1, 2)//window;
在非严格模式下, call 传递的第一个参数如果是 undefined 或者 null, 那 this 会自动替换为 Window 对象
function fn(a, b) {
console.log(this)
}
fn(1, 2)//window
//等价于
fn.call(undefined, 1, 2)//window
fn.apply(undefined, [1, 2])//window
more
xx.yy可以理解为yy的this绑定在了xx
var obj = {
fn: function (a, b) {
console.log(this)
},
child: {
fn2: function () {
console.log(this)
}
}
}
obj.fn(1, 2)//obj
//等价于
obj.fn.call(obj, 1, 2) // 所以 this 是 obj
obj.fn.apply(obj, [1, 2])//obj
obj.child.fn2()//obj.child 上一级?
//等价于
obj.child.fn2.call(obj.child) // 所以 this 是 obj.child
let name = 'hello';
let people = {
name: 'world',
sayName: function () {
console.log(this.name)
}
}
let sayAgain = people.sayName;
function sayName() {
console.log(this)
console.log(this.name)
}
sayName()//hello 可不可以理解为window.sayName
//解析:相当于 sayName.call(undefined)
//因为是非严格模式,所以 this 被替换成 Window
people.sayName()//world
//解析: 相当于 `people.sayName.call(people)`
sayAgain()//hello
//解析: 相当于 `sayAgain.call(undefined)`
//this是在运行时进行绑定的,并不是在编写时绑定的
//因为是非严格模式,所以 this 被替换成 Window
var arr = []
for (var i = 0; i < 3; i++) {
arr[i] = function () { console.log(this) }
}
var fn = arr[0]
arr[0]()//arr
/*
解析: 因为函数是个特殊的对象,
所以 arr 相当于 { '0': function(){}, '1': function(){}, '2': function(){}, length:3}
arr[0]相当于 `arr['0']` 相当于 `arr.0` (当然这种写法不符合规范),
所以 arr[0]等价于 arr.0.call(arr), this就是 arr
*/
fn()//window
/*
解析: 相当于 `fn.call(undefined)`, 所以 fn 里面的 this 是 Window
*/
实际点的例子
var a = {
name: 'js',
log: function () {
//所以这里的
console.log(this)//{name: "js", log: ƒ}
function setName() {
this.name = 'javaScript'
console.log(this) //window
}
setName()//而这里相当于window.setName() 或者 setName.call(undefined) ?
}
}
a.log() //log的this绑定到了a
bind
bind 的作用和 call 与 apply 类似,区别在于使用上,bind 的执行的结果返回的是绑定了一个对象的新函数.
这也就是为什么对函数里传参时,要使用bind而不是call/apply了,
call实际上在绑定的同时执行了,而bind才是返回绑定后的函数。
var obj = { name: 'qm' }
function sayName() {
console.log(this.name)
}
// 这里 fn 还是一个函数,功能和 sayName 一模一样,
//区别只在于它里面的 this 是 obj
var fn = sayName.bind(obj)
fn() // qm
var fn2 = sayName.call(obj);
//fn2()//error
var app = {
container: document.querySelector('body'),
bind: function () {
//点击的时候会执行 sayHello,
//sayHello 里面的 this 代表 body 对象
this.container.addEventListener('click', this.sayHello)
//点击的时候会执行 sayHello,
//sayHello 里面的 this 代表 app 对象
this.container.addEventListener('click', this.sayHello.bind(this))
},
sayHello: function () {
console.log(this)
}
}
app.bind()
new
this是在运行时进行绑定的,并不是在编写时绑定的
function foo() {
console.log(this.a)
}
var a = 2
foo()//2 这里的this指向window
var obj = {
a: 3,
foo: foo
}
obj.foo() //3 这里的this指向obj
//这里类似把foo.bind(c)了??
var c = new foo()//undefined
c.a = 4
console.log(c.a)//4
箭头函数
箭头函数其实是没有 this 的,其取决于所在的环境下的 this。
也可以理解为:这个函数中的 this 只取决于他外面的第一个不是箭头函数的函数的 this。
this 一旦绑定了上下文,就不会被任何代码改变(如bind的使用)。
感觉箭头函数很像是被bind后生成的函数。
let app = {
fn1: function (a) {
console.log(this)
},
fn2(a) {
console.log(this)
},
fn3: (a) => {
console.log(this)
}
}
app.fn1();//app
app.fn2();//app
app.fn3();//window
app.fn2.call(app)
app.fn3.call(window)
function a() {
return () => {
return () => {
console.log(this)//window
}
}
}
console.log(a()()()) //return undefined?
var app = {
init() {
//这一层的this 是app
var menu = {
init: () => {
console.log(this) //app
},
bind() {
console.log(this)//menu
}
}
menu.init()
/*相当于 menu.init.call(menu 所在的环境下的 this) , 所以 init 里面的 this 也就是 app。
*/
menu.bind()
/*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
*/
}
}
app.init()
var app = {
init: () => {
//这一层的this 是window
var menu = {
init: () => {
console.log(this) //window
},
bind() {
console.log(this)//menu
}
}
menu.init()
/*相当于 menu.init.call(menu 所在的环境下的 this) , 所以 init 里面的 this 也就是 app。
*/
menu.bind()
/*相当于 menu.bind.call(menu),也就是 menu,所以 bind 里面的 this 就是 menu
*/
}
}
app.init()
var app = {
fn1() {
setTimeout(function () {
console.log(this)
}, 10)
//过10ms 后执行
//fn.call(undefined) ,所以输出 Window
},
fn2() {
setTimeout(() => {
console.log(this)
}, 20)
//过20ms 执行箭头函数
//箭头函数里面没资格有 自己的 this,借用 setTimeout 外面的 this,也就是 app
},
fn3() {
setTimeout((function () {
console.log(this)
}).bind(this), 30)
// 创建了一个新函数,这个新函数里面绑定了 外面的this,也就是 app
// 20 ms 后执行新函数,输出 this,也就是刚刚绑定的 app
},
fn4: () => {
setTimeout(() => {
console.log(this)
}, 40)
//过40ms 执行箭头函数
//箭头函数里面没资格有 this,用 setTimeout 外面的 this
//setTimeout 所在的 fn4也是箭头函数,没资格拥有自己的 this,借用外面的 this ,
//app的this是 Window
}
}
app.fn1()//window
app.fn2()//app
app.fn3()//app
app.fn4()//window