标题this指向的应用场景
1.this在全局作用域下(全局函数) //window
<script>
console.log(this);//Window
</script>
}
2.1.在严格模式下,普通的this指向,指向的是window
<script>
'use strict'
console.log(this);//Window
</script>
}
2.2在严格模式下,函数中的this指向,指向的是undefined
<script>
'use strict'
function name(params) {
console.log(this);
}
name()
</script>
//undefined
3.在函数中使用this
3.1当某个函数为对象的一个属性时,在这个函数内部this指向这个对象
// 1.案例一:
var obj = {
name: "why",
foo: foo
}
obj.foo() // obj对象
3.2 切记 双重调用 函数()()fn又会变成独立函数 fn函数的调用为window this又变成window
const obj = {
fun2: function () {
console.log(1111, this); //object
return function () {
console.log(this); //widow
};
},
};
obj.fun2(); //object
let f2 = obj.fun2();
obj.fun2()(); //object widow
f2(); //object widow
4.this出现在构造函数中,指向构造函数新创建的对象也就是造函数的实例
原理:每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样
// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this); //person
}
// 每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样
var p1 = new Person("why", 18);
console.log(p1.name, p1.age);
var p2 = new Person("kobe", 30);
console.log(p2.name, p2.age);
var obj = {
foo: function () {
console.log(this);
},
too: function () {
console.log(this);
},
};
// 这属于new绑定和obj绑定的冲突,我们需要考虑优先级的问题 后面再将
new obj.foo();
obj.too();
5.当一个元素被绑定事件处理函数,this指向被点击的这个元素
var btn = document.querySelector("button");
btn.onclick = function () {
console.log(this); //<button>this <button>>
};
6.箭头函数中this指向父级外层作用域的 this,没有沿着作用域链查找 ,直到为window
var obj = {
foo() {
console.log(this);
},
bar: () => {
console.log(this);
}
}
obj.foo() // {foo: ƒ, bar: ƒ}
obj.bar() // window
标题–执行顺序
1.所有的函数在被调用时,都会创建一个执行上下文:
2.这个上下文中记录着函数的调用栈、AO对象等;
3.this也是其中的一条记录
标题this的启示
1.函数在调用时,JavaScript会默认给this绑定一个值;
2.this的绑定和定义的位置(编写的位置)没有关系;
3.this的绑定和调用方式以及调用的位置有关系;
4.this是在运行时被绑定的;
标题this的绑定规则
绑定一:默认绑定;
独立函数调用
独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用
// 默认绑定: 独立函数调用 // 1.直接调用这个函数 独立函数的调用 指向全局对象window
// 1.案例一:
// function foo() {
// console.log(this)
// }
// foo()
// 2.案例二: 这三个函数的是独立的 独立函数的调用 指向全局对象window
// function foo1() {
// console.log(this)
// }
// function foo2() {
// console.log(this)
// 前面没有对象调用 说明是个独立函数 指向全局对象window
// foo1()
// }
// function foo3() {
// console.log(this)
// 前面没有对象调用 说明是个独立函数 指向全局对象window
// foo2()
// }
// foo3()
// 3.案例三:
// var obj = {
// name: "why",
// foo: function() {
// console.log(this)
// }
// }
// var bar = obj.foo
// bar() // window bar()看函数是如何被调用的,这个是没有函数的调用主题。其实主要是看的是在调用的时候有没有主题,并不是在定义的时候看有没有主题 ,
// 所以bar函数在调用的时候有没有主题也是独立函数
// 4.案例四:
// function foo() {
// console.log(this)
// }
// var obj = {
// name: "why",
// foo: foo
// }
// var bar = obj.foo
// bar() // window
// 5.案例五:
function foo() {
function bar() {
console.log(this);
}
return bar;
}
var fn = foo();
fn(); // window
var obj = {
name: "why",
eating: fn,
};
obj.eating(); // 结果this是obj obj是这个函数的调用主题 隐式绑定
绑定二:隐式绑定;
通过某个对象进行调用的
也就是它的调用位置中,是通过某个对象发起的函数调用。
// 隐式绑定: object.fn()
// object对象会被js引擎绑定到fn函数的执行上下文中this里面
function foo() {
console.log(this);
}
// 独立函数调用
// foo()
// 1.案例一:
// var obj = {
// name: "why",
// foo: foo
// }
// obj.foo() // obj对象
// 2.案例二:
// var obj = {
// name: "why",
// eating: function() {
// console.log(this.name + "在吃东西")
// },
// running: function() {
// console.log(obj.name + "在跑步")
// }
// }
// obj.eating() //this就是obj对象
// obj.running() //this就是obj对象
// var fn = obj.eating 如果这样那,fn又会变成独立函数
// fn() fn函数的调用为window this又变成window
// 3.案例三:
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
},
};
var obj2 = {
name: "obj2",
bar: obj1.foo,
};
obj2.bar(); //结果为obj2 bar: obj1.foo函数是被obj2调用起来的 所以this指向obj2
// 总 结 隐式绑定: object.fn()--就是需要有对象主题来 调用起来 的函数 这个过程就叫做隐式绑定:
// 隐式绑定:就会自动将对象绑定到函数里面的this
绑定三:显示绑定;
隐式绑定有一个前提条件:
1.必须在调用的对象内部有一个对函数的引用(比如一个属性);
2. 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
3.正是通过这个引用,间接的将this绑定到了这个对象上;
call、apply、bind 我们明确的绑定了this指向的对象,所以称之为 显示绑定
call、apply第一个参数是相同的,后面的参数,apply为数组,call为参数列表;
// function foo() {
// console.log("函数被调用了", this)
// }
// foo.call() //结果 打印--console.log("函数被调用了", this)
// 1.foo直接调用和call/apply调用的不同在于this绑定的不同
// foo直接调用指向的是全局对象(window)
// foo()
// var obj = {
// name: "obj"
// }
// call/apply是可以手动指定this的绑定对象--指定任何绑定对象都可以 call/apply是js内部帮助我们定义实现过的函数
// foo.call(obj)
// foo.apply(obj)
// foo.apply("aaaa")
// 2.call和apply有什么区别?
function sum(num1, num2, num3) {
console.log(num1 + num2 + num3, this);
}
// call和apply传参数的方式是不同的,
// call 第一个参数是自己手动绑定的this的指向,后面的参数叫做剩余参数,是通过,号进行分割
sum.call("call", 20, 30, 40);
// apply 第一个参数是自己手动绑定的this的指向 第二个参数是数组形式,将所有参数放到数组中
sum.apply("apply", [20, 30, 40]);
// 3.call和apply在执行函数时,是可以明确的绑定this, 这个绑定规则称之为显示绑定
如果我们希望一个函数总是显示的绑定到一个对象上
bind的绑定会形成一个新的函数 调用的时候新的函数,新函数就永远绑定的就是我们自己指定绑定的this对象了
function foo() {
console.log(this);
}
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// foo.call("aaa")
// 默认绑定和显示绑定bind冲突: 优先级(显示绑定优先级更高)
// bind的绑定会形成一个新的函数 调用的时候新的函数,新函数就永远绑定的就是我们自己指定绑定的this对象了
// newFoo又是一个新函数
var newFoo = foo.bind("aaa");
newFoo();
newFoo();
newFoo();
newFoo();
newFoo();
newFoo();
var bar = foo;
console.log(bar === foo);
console.log(newFoo === foo);
绑定四:new绑定
JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字
使用new关键字来调用函数是,会执行如下的操作:
1.创建一个全新的对象;
2.这个新对象会被执行prototype连接;
3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
4.如果函数没有返回其他对象,表达式会返回这个新对象;
// 我们通过一个new关键字调用一个函数时(构造器), 这个时候this是在调用这个构造器时创建出来的对象
// this = 创建出来的对象
// 这个绑定过程就是new 绑定
function Person(name, age) {
this.name = name;
this.age = age;
console.log(this); //person
}
// 每次new的时候就会创建一个新的对象 对象名里面默认就绑定的有this属性 new Person就会将我们的参数放到函数Person中创建一个新对象,所以每次new出来的值都不一样
var p1 = new Person("why", 18);
console.log(p1.name, p1.age);
var p2 = new Person("kobe", 30);
console.log(p2.name, p2.age);
var obj = {
foo: function () {
console.log(this);
},
too: function () {
console.log(this);
},
};
// 这属于new绑定和obj绑定的冲突,我们需要考虑优先级的问题 后面再将
new obj.foo();
obj.too();
标题规则优先级
1.默认规则的优先级最低
毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
2.显示绑定优先级高于隐式绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
obj.foo();
// 1.call/apply的显示绑定高于隐式绑定
// obj.foo.apply('abc')
// obj.foo.call('abc')
// 2.bind的优先级高于隐式绑定
// var bar = obj.foo.bind("cba")
// bar()
// 3.更明显的比较
function foo() {
console.log(this);
}
var obj = {
name: "obj",
foo: foo.bind("aaa"),
};
obj.foo(); //绑定显示绑定 [String: 'aaa']
3.new绑定优先级高于隐式绑定
var obj = {
name: "obj",
foo: function () {
console.log(this);
},
};
// new的优先级高于隐式绑定
var f = new obj.foo();
4.new绑定优先级高于bind
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
new绑定可以和bind一起使用,new绑定优先级更高
// 结论: new关键字不能和apply/call一起来使用
// new的优先级高于bind
function foo() {
console.log(this);
}
var bar = foo.bind("aaa");
bar();//String {'aaa'}
var obj = new bar();// foo{}
// new绑定 > 显示绑定(apply/call/bind) > 隐式绑定(obj.foo()) > 默认绑定(独立函数调用)
标题注意点
1.如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则
function foo() {
console.log(this);
}
foo.apply("abc");
foo.apply({});
// apply/call/bind: 当传入null/undefined时, 自动将this绑定成全局对象
// foo.apply(null);
// foo.apply(undefined);
var bar = foo.bind(null);
bar();
2.创建一个函数的 间接引用,这种情况使用默认绑定规则。
赋值(obj2.foo = obj1.foo)的结果是foo函数;
foo函数被直接调用,那么是默认绑定;
// 争论: 代码规范 ;
var obj1 = {
name: "obj1",
foo: function () {
console.log(this);
},
};
var obj2 = {
name: "obj2",
};
// obj2.bar = obj1.foo
// obj2.bar()
(obj2.bar = obj1.foo)(); // 浏览器中是window node中是global
标题this规则之外 – ES6箭头函数
箭头函数不使用this的四种标准规则(也就是不绑定this),而是根据外层作用域来决定this。
// 1.测试箭头函数中this指向
// var name = "why"
var foo = () => {
console.log(this);
};
// 箭头函数没有this,唯一确定this的方式是从上级作用域中寻找
foo();
var obj = { foo: foo };
obj.foo();
foo.call("abc");
// 2.应用场景
var obj = {
data: [],
getData: function () {
// 发送网络请求, 将结果放到上面data属性中
// 在箭头函数之前的解决方案
// var _this = this
// setTimeout(function() {
// var result = ["abc", "cba", "nba"]
// _this.data = result
// }, 2000);
// 箭头函数之后 this的指向在上级作用域中寻找,这里面的this就是指向getDate函数,因为getDate函数是setTimeout的上级作用域
// getData是普通函数 又因为obj.getData()通过隐式绑定 绑定到obj,所以这里面的this就变成了obj对象,就可以调用date了
setTimeout(() => {
var result = ["abc", "cba", "nba"];
this.data = result;
console.log(this);
}, 2000);
},
};
obj.getData();