学习了coderwhy的JavaScript高级语法视频课的笔记
如有错误或者不合适的地方,敬请见谅,欢迎指出和拓展,谢谢各位了
一、为什么需要this
- 在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是JavaScript中的this和常见的面向对象语言中的this不太一样:
- 常见面向对象的编程语言中,比如Java、C++、Swift、Dart等等一系列语言中,this通常只会出现在类的方法中;
- 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象;
- 但是JavaScript中的this更加灵活,无论是它出现的位置还是它代表的含义。
- 我们来看一下编写一个obj的对象,有this和没有this的区别
<script>
// 没有使用this
var obj = {
val: 'obj的对象',
f: function () {
console.log(obj.val + "执行了")
}
}
obj.f()
// 使用了this
var obj2 = {
val2: 'obj2的对象',
f2: function () {
console.log(this.val2 + "执行了")
}
}
obj2.f2()
</script>
二、this指向
- this在全局作用域下指向的就是window。但是,开发中很少直接在全局作用于下去使用this,通常都是在函数中使用。
- 所有的函数在被调用时,都会创建一个执行上下文;
- 这个上下文中记录着函数的调用栈、AO对象等;
- this也是其中的一条记录。
- 接下来,我们认识一下this指向的四个绑定规则,搞懂了也就基本掌握了this的指向问题:
- 绑定规则一:默认绑定,也就是独立函数调用
<script>
// 案例一
function foo () {
console.log(this)//window
}
foo()
// 案例二
function test1 () {
console.log(this)//window
}
function test2 () {
console.log(this)//window
test1()
}
function test3 () {
console.log(this)//window
test2()
}
test3()
//案例三
function foo3 (func) {
func()
}
var obj = {
name: 'obj',
bar: function () {
console.log(this)//window
}
}
foo3(obj.bar)
</script>
- 绑定规则二:隐式绑定,也就是它的调用位置中,是通过某个对象发起的函数调用
<script>
// 案例一
function foo () {
console.log(this)//obj{}
}
obj = {
name: 'obj',
foo: foo
}
obj.foo()
// 案例二
function foo2 () {
console.log(this)//obj2{}
}
obj2 = {
name: 'obj2',
foo2: foo2
}
obj3 = {
name: 'obj3 ',
obj2: obj2
}
obj3.obj2.foo2()
//案例三(独立函数调用)
function foo3 (func) {
console.log(this)//window
}
var obj4 = {
name: 'obj4',
foo3: foo3
}
var bar = obj4.foo3
bar()//独立函数调用
</script>
- 绑定规则三:显示绑定,也就是通过call()、apply()和bind()绑定this对象。 这里就不详细讲这三个方法的具体用法了。
<script>
// 案例一:call()
function foo () {
console.log(this)
// this的指向:
// window
// { name: '一个obj' }
// String {'abc'}
}
foo.call(window)
foo.call({ name: '一个obj' })
foo.call('abc')
// 案例二:apply()
function foo2 () {
console.log(this)
// this的指向:
// window
// { name: '一个obj' }
// String {'abc'}
}
foo2.apply(window)
foo2.apply({ name: '一个obj' })
foo2.apply('abc')
// 案例三:bind()——总是显示绑定到一个对象上
function foo3 () {
console.log(this)//{name: 'obj', foo3: ƒ}
}
var obj = {
name: 'obj',
foo3: foo3
}
var bar = foo3.bind(obj)
bar()
</script>
- 绑定规则四:new绑定。JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字
- 使用new关键字来调用函数是,会执行如下的操作:
- 1.创建一个全新的对象;
- 2.这个新对象会被执行prototype连接;
- 3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
- 4.如果函数没有返回其他对象,表达式会返回这个新对象;
- 使用new关键字来调用函数是,会执行如下的操作:
<script>
function Person (name) {
console.log(this)//Person {}
this.name = name
}
var p = new Person('aaa')
console.log(p)//Person {name: 'aaa'}
</script>
- 内置函数的绑定 有些时候,我们会调用一些JavaScript的内置函数,或者一些第三方库中的内置函数。这些内置函数会要求我们传入另外一个函数,我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库内部会帮助我们执行,这些函数中的this又是如何绑定的呢?
- 案例:setTimeout、数组的forEach、div的点击
<script>
// 案例一
setTimeout(function () {
console.log(this)//window
}, 1000)
// 案列二
var arr = ['a', 'b', 'c', 'd', 'e']
var obj = {
name: 'obj'
}
arr.forEach(function () {
console.log(this)//五次obj对象
}, obj)
//案例三
var box = document.querySelector('.box')
box.onclick = function () {
console.log(this === box)//this就是指向box
}
</script>
- this绑定规则的优先级总结
- 默认规则(函数独立调用)的优先级最低
- 显示绑定优先级高于隐式绑定
- new绑定优先级高于隐式绑定
- new绑定优先级高于bind
- new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
- new绑定可以和bind一起使用,new绑定优先级更高
- this绑定规则之外
- 忽略显示绑定:如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则(函数独立调用)。
- 间接函数引用:创建一个函数的 间接引用,这种情况使用默认规则(函数独立调用)。
<script>
function foo () {
console.log(this)
}
var obj1 = {
name: 'obj1',
foo: foo
}
var obj2 = {
name: 'obj2'
}
obj1.foo()//obj1
; (obj2.foo = obj1.foo)()//window
</script>
- ES6箭头函数:箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。
三、面试题
1.
<script>
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
}
function sayName () {
var sss = person.sayName;
sss(); // window
person.sayName(); // person
(person.sayName)(); // person
(b = person.sayName)(); // window
}
sayName()
</script>
2.
- 注意:
- var person1={...}不是块级作用域;
- 箭头函数没有this,向上父级作用域找(父级作用域看函数定义位置,而不是调用所在位置)
<script>
var name = 'window'
var person1 = {
name: 'person1',
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: 'person2' }
person1.foo1(); // person1
person1.foo1.call(person2); // person2
person1.foo2() // window
person1.foo2.call(person2) // window
person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2
person1.foo4()()// person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
</script>
3.
- 注意:
- 箭头函数没有this,向上父级作用域找(父级作用域看函数定义位置,而不是调用所在位置)
<script>
var name = '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) //person1
person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
</script>
4.
- 注意:
- 箭头函数没有this,向上父级作用域找(父级作用域看函数定义位置,而不是调用所在位置)
<script>
var name = '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()() // window
person1.obj.foo1.call(person2)() //window
person1.obj.foo1().call(person2) // person2
person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)()//person2
person1.obj.foo2().call(person2)//obj
</script>