一、this 绑定
总的记住一个口诀,有引用的地方this指向引用对象,没有引用的地方this指向window
this 是如何绑定的?
1、默认绑定(函数的 this 会默认绑定到全局对象 window 上)
csharp
复制代码
function foo (){
console.log(this.a)
}
let a = 1
foo() // 1
2、对象绑定
调用位置是否有上下文对象,或者被某个对象拥有或包含。
javascript
复制代码
function foo(){
console.log(this.a)
}
let obj = {
a: 2,
foo:foo
}
let a = 1
obj.foo(); // 2
function foo(){
console.log(this.a)
}
let obj1 = {
a: 2,
foo: foo
}
let obj2 = {
a: 3,
obj1: obj1
}
let a = 1
obj2.obj1.foo(); // 2
3、call等改变对象指向
javascript
复制代码
function foo(){
console.log(this.a)
}
let obj = {
a: 1
}
foo.call(obj)//1
4、new对象
new 出来的函数 this 绑定的是新创建的对象
javascript
复制代码
function Foo(a){
this.a = a
}
let bar = new Foo(2)
console.log(bar.a) // 2
this 绑定优先级
默认绑定的优先级是最低的
new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
1、显示绑定 VS 隐式绑定
javascript
复制代码
function foo(){
console.log(this.a)
}
let obj1 = {
a: 1,
foo: foo
}
let obj2 = {
a: 2
}
console.log(obj1.foo()) // 1
obj1.foo.call(obj2) // 2
通过以上代码我们可以看到 显式绑定 的优先级高于 隐式绑定
2、显示绑定 VS new 绑定
javascript
复制代码
function foo(a){
this.a = a
}
let obj1 = {
foo
}
let bar = foo.bind(obj1)
bar(2)
console.log(obj1.a) // 2
let bar2 = new bar(3)
console.log(obj1.a) // 2
console.log(bar2.a) // 3
new 修改了显示绑定 调用 bar 中的 this,所以 new 绑定的优先级高于显式绑定
二、this 指向
判断准则
第一准则:this 永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。(不包含箭头函数)
第二准则:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
判断顺序
- 函数是否在 new 中调用,如果是的话 this 绑定的是新创建的对象;
- 函数是否通过 call、apply、bind 的方式调用,如果是的话 this 绑定的是指定的对象;
- 函数是否在某个上下文中被调用,如果是的话 this 绑定的是函数调用的上下文;
- 除此之外 this 绑定的就是全局对象 在严格模式下绑定的是 undefined。
常见的指向问题
- 箭头函数没有自己的 this 指针(需要从执行上下文来进行判断)
三、改变 this 指向
有四种方式
- 变量保存 this:将 this 临时保存下来
- call() :使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
- bind() :会有一个返回值,返回值是一个拥有第一个函数作用域的新的函数体
- apply() :调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。
变量保存 this
javascript
复制代码
var _this = window;
var obj = {
name:"张三",
show:function(){
console.log(this) //obj
console.log(_this) // window
}
}
obj.show()
call
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。
javascript
复制代码
函数名称.bind(参数1,参数2,a,b,c.....)
参数1:当前函数的作用域
参数2:需要传递的参数,参数是一个一个传
function fn(a,b){
console.log(this,a,b)
}
document.onclick = functioin(){
fn.call(document,1,2)
}
可以使用 call 来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
ini
复制代码
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}
var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);
bind
bind 创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
特性就是会有一个返回值,返回值是一个拥有第一个函数作用域的新的函数体
javascript
复制代码
函数名称.bind(参数1,参数2.....)() // 必须调用一下
参数1:当前函数的作用域
参数2:需要传递的参数
var obj = {
name:"张三",
show:function(val){
console.log(this) //obj 没有修改前
console.log(this,1) // document 1 修改后
}
}
obj.show().bind(document,1)()
MDN:ECMAScript 5 引入了 Function.prototype.bind()。调用 f.bind(someObject) 会创建一个与 f 具有相同函数体和作用域的函数,但是在这个新函数中,this 将永久地被绑定到了 bind 的第一个参数,无论这个函数是如何被调用的。
apply
apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。
javascript
复制代码
函数名称.bind(参数1,[参数2,a,b,c.....])
参数1:当前函数的作用域
参数2:需要传递的参数 数组
function fn(a,b,c){
console.log(this,a,b,c)
}
document.onclick = functioin(){
fn.apply(document,[1,2,3])
}
call ,apply ,bind 三者的区别
不同点:
-
bind 会有一个返回值,返回值是函数体,因此需要加上 () 才能调用
-
call,apply 是没有返回值的,当改变函数 this 指向的时候,函数就会执行,不需要加 () 调用
-
call 传递参数的时候是一个一个传递的
-
apply 是传递一个数组或者类数组对象的参数