this指向
this的绑定和调用方式以及调用的位置有关系
this是在运行时被绑定的
- 默认绑定
<script>
// 严格模式,独立函数调用:undefined
"use strict"
function foo() {
console.log(this);
}
// 1. 默认绑定(独立函数调用)
foo() // window
// 2. 函数定义在对象中,但是独立调用
var info = {
name: 'qqq',
baz: function () {
console.log('我是baz函数', this);
}
}
var bar = info.baz
bar() // window(独立函数调用)
info.aaa = foo
info.aaa() // obj对象
// 3. 高阶函数
function test(fn) {
fn()
}
test(info.baz) // window
</script>
- 隐式绑定
// 隐式绑定
function foo() {
console.log(this);
}
var obj = {
bar : foo
}
obj.bar() // obj对象
var obj2 = {
bar2 : obj
}
obj2.bar2.bar() // obj对象
- new 绑定
/* new操作符做的事
1. 创建一个空对象
2. 将this指向这个空对象
3. 执行函数体的代码
4. 如果函数没有返回其他对象,表达式会返回这个新对象
*/
function foo() {
console.log(this);
}
new foo() // foo对象
- 显示绑定
function foo() {
console.log(this);
}
var obj = {
name:'www'
}
// 执行函数,强制this就是obj对象
foo.call(obj) // obj对象
foo.apply(obj) // obj对象
foo.call(123) // Number{123} 包装类
foo.call(undefined) // window
// 调用foo时,不希望obj对象上有函数
var bar = foo.bind(obj) // bind() 方法创建一个新的绑定函数
bar() // obj对象
call 和 apply 的区别
function foo(name,age,create) {
console.log(this);
console.log('参数列表:'+ name,age,create); // wwqq 20 新增
}
var obj = {name: 'wqq'}
/*
第一个参数:绑定的this
第二个参数:以数组的形式传入实参
*/
foo.apply('apply',['www',19])
/*
第一个参数:绑定的this
第二个参数:以参数列表的形式传入实参
*/
foo.call('call','qqq',20)
/* 了解 */
var bar = foo.bind(obj,'wwqq',20)
bar('新增') // obj对象
内置函数的调用绑定
<button>按钮</button>
setTimeout(function () {
console.log('定时器函数:',this); // window
},1000)
var btnEl = document.querySelector('button')
btnEl.onclick = function () {
console.log('btn的点击', this); // <button>按钮</button>
}
btnEl.addEventListener('click',function () {
console.log('btn的点击', this); // <button>按钮</button>
})
var names = ['ww','qq','wqq']
names.forEach(function (item) {
console.log('forEach', this); // window
})
names.forEach(function (item) {
console.log('forEach', this); // aaa
},'aaa')
this绑定优先级
- 显示绑定 > 隐式绑定
- new绑定 > 隐式绑定
- new绑定 > bind
- bind > apply / call
function foo() {
console.log(this);
}
// 1. 显示绑定 > 隐式绑定
var bar = foo.bind('bbb')
var obj = {
foo: foo,
objBar: bar,
baz: function () {
console.log('我是baz函数:', this);
}
}
obj.foo.apply('abc') // abc
obj.objBar() // bbb
// 2. new绑定 > 隐式绑定
new obj.baz() // baz{}
// 3. new绑定 > bind
/* 3.1 new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
3.2 new绑定可以和bind一起使用,new绑定优先级更高
*/
var bindFn = foo.bind('ccc')
new bindFn() // foo{}空对象
// 4. bind > apply / call
var bindFoo = foo.bind('ddd')
bindFoo.apply('jjj') // ddd
bindFoo.call('kkk') // ddd
- 忽略显示绑定
function foo() {
console.log(this);
}
// 如果在显示绑定,传入一个null或者undefined,则使用默认绑定
foo.apply('abc') // abc
foo.apply(null) // window, 严格模式:null
foo.apply(undefined) // window, 严格模式:undefined
- 间接函数引用
var obj1 = {
name:'obj1',
foo: function () {
console.log('foo:',this);
}
}
var obj2 = {
name:'obj2'
};
(obj2.foo = obj1.foo)() // window
obj2.foo = obj1.foo
obj2.foo() // obj2
箭头函数(arrow function)
- 箭头函数不会绑定this、arguments属性
- 箭头函数不能作为构造函数来使用(不能和new一起来使用,会抛出错误)
var baz = (name) => {
console.log('箭头函数的函数体');
}
var names = ['aaa', 'bbb', 'ccc']
names.forEach((item, index, arr) => {
console.log(item, index, arr);
})
// 1. 如果只有一个参数, ()可以省略
names.forEach(item => {
console.log(item);
})
// 2. 如果函数执行体中只有一行代码, 那么可以省略大括号
names.forEach(item => console.log(item))
// 3. 只有一行代码时,这行代码的返回值会作为整个函数的返回值
var nums = [12, 32, 43]
var newNum = nums.filter(item => item % 2 === 0)
// 4. 如果默认返回是一个对象, 那么需要给这个对象加上()
var arrFn = () => ({name: 'www'})
arrFn() // {name: 'www'}
// 实现所有偶数平方的和
var sumNum = nums.filter(item => item % 2 === 0)
.map(item => item ** 2)
.reduce((pre, item) => pre + item)
- 箭头函数的this
// 查找规则 1
var baz = () => {
console.log(this);
}
baz.apply('aaa') // window
// 查找规则 2
var obj = {
name:'obj',
foo:function () {
var bar = () => {
console.log('bar函数',this);
}
return bar
}
}
var fn = obj.foo()
fn.apply('bbb') // obj对象
面试题
// 面试题一:
var name = 'this window'
var parson = {
name:'parson',
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = parson.sayName
sss() // 独立函数调用: this window
parson.sayName(); // 隐式绑定: parson
(parson.sayName)(); // 隐式绑定: parson
(b = parson.sayName)() // 间接函数引用:this window
}
sayName()
// 面试题二:
var name = 'this window'
// 对象是没有作用域的
var person1 = {
name: 'parson1',
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name);
}
},
foo4: function () {
return () => {
console.log(this.name);
}
}
};
var person2 = {name: 'parson2'}
person1.foo1() // 隐式绑定:parson1
person1.foo1.call(person2) // 显示绑定:person2
person1.foo2() // 上层作用域:this window
person1.foo2.call(person2) // 上层作用域:this window
person1.foo3()() // 独立函数调用:this window
person1.foo3.call(person2)() // 独立函数调用:this window
person1.foo3().call(person2) // 显示绑定:person2
person1.foo4()() // 上层作用域:parson1
person1.foo4.call(person2)() // 上层作用域:parson2
person1.foo4().call(person2) // 上层作用域:person1
// 面试题三
var name = 'this window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name);
}
this.foo2 = () => console.log(this.name)
this.foo3 = function () {
return function () {
console.log(this.name);
}
}
this.foo4 = function () {
return () => {
console.log(this.name);
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // 隐式绑定:person1
person1.foo1.call(person2) // 显示绑定:person2
person1.foo2() // 上层作用域:person1
person1.foo2.call(person2) // 上层作用域:person2
person1.foo3()() // 独立函数调用:this window
person1.foo3.call(person2)() // 独立函数调用:this window
person1.foo3().call(person2) // 显示绑定:person2
person1.foo4()() // 上层作用域(隐式):parson1
person1.foo4.call(person2)() // 上层作用域(显示):parson2
person1.foo4().call(person2) // 上层作用域(隐式):person1
// 面试题四
var name = 'this window'
function Person(name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name);
}
},
foo2: function () {
return () => {
console.log(this.name);
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.obj.foo1()() // 独立函数调用:this window
person1.obj.foo1.call(person2)() // 独立函数调用:this window
person1.obj.foo1().call(person2) // 显示绑定:person2
person1.obj.foo2()() // 上层作用域(隐式):obj
person1.obj.foo2.call(person2)() // 上层作用域(显示):parson2
person1.obj.foo2().call(person2) // 上层作用域(隐式):obj