1.this的初衷
this设计的初衷是在函数内部使用,用来指代当前的运行环境。为什么这么说呢?
JavaScript中的对象的赋值行为是将地址赋给一个变量,引擎在读取变量的时候其实就是要了个地址然后再从原始地址中读取对象。而JavaScript 允许函数体内部引用当前环境的其他变量,而这个变量是由运行环境提供的。由于函数又可以在不同的运行环境执行(如全局作用域内执行,对象内执行…),所以需要一个机制来表明代码到底在哪里执行!于是this出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
2.function中的this
对于函数中的this的指向问题,有一句话很好用:运行时this永远指向最后调用它的那个对象。
例题1
<script>
function f() {
console.log(this); // this是谁必须等调用了才能知道
}
// f(); // 普通函数调用 里面的this表示window
let obj = {};
obj.g = f;
obj.g();
</script>
1)如果this出现在普通的函数中,this表示window,如果你通过window打点调用一个函数,这个函数中的this也是window
IIFE中的this
<script>
(function () {
console.log(this);
})(); // 在非严格模式下,IIFE中的this表示window
</script>
在严格模式下调用一个普通函数,this表示und。IIFE中的this也表示und
3.在绑定事件中的this
事件绑定,事件处理程序,事件发生时,浏览器帮我们调用这个函数,此函数中的 this表示事件源
<button id="box">box</button>
<script>
let box = document.getElementById("box");
function f(){
console.log(this); // window
return function g(){
console.log(this); // 事件源
}
}
box.onclick = f();
</script>
4.在对象中的this
在一个对象中,如果有方法(函数),如果通过这个对象调用方法,方法中的this表示这个对象
例题2
var wc = {
name:"wangcai", // 叫属性 var name = "wangcai"
age:100, // 叫属性
eat:function () { // 叫方法(属性)
console.log("eat...")
console.log(this);
}
}
// 一个方法中的this是谁,就看点前面是谁。
wc.eat(); // 调用一个对象上的方法
例题3
<script>
var number = 1;
var obj = {
number:2,//GO里
showNumber:function(){
this.number = 3;
(function(){
console.log(this.number);//立即调用函数this指向window
})();
console.log(this.number);//对象打点调用 this=3
}
};
obj.showNumber();
</script>
特例
<script>
var a= 20;
var obj={
a:10,
c:this.a+10,//不是方法 里面的this值window
fn: function(){
return this.a;//对象里的this
}
}
console.log(obj.c);//30
console.log(obj.fn())//10
</script>
5.构造函数中的this
所谓构造函数,就是通过这个函数生成一个新对象(object)。当一个函数作为构造器使用时(通过 new 关键字), 它的 this 值绑定到新创建的那个对象。如果没使用 new 关键字, 那么他就只是一个普通的函数, this 将指向 window 对象
new 运算符 肯定干几件事
// 1)在构造器内部创建一个空对象
// 2)让构造器中的this指向这个对象
// 3)返回这个对象
<script>
function NBAPlayer(name,age) { // 类
this.name = name;
this.age = age;
}
var p1 = new NBAPlayer("乔丹",10);
var p2 = new NBAPlayer("科比",20);
console.log(p1.name);
console.log(p1.age);
console.log(p2.name);
console.log(p2.age);
</script>
6.箭头函数中的this
es5中的this要看函数在什么地方调用(即要看运行时),通过谁是最后调用它该函数的对象来判断this指向。但es6的箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this,否则,this 为 undefined。箭头函数的 this 始终指向函数定义时的 this,而非执行时。
例题
<script>
var name="wangcai"
let obj = {
name: "Jake",
func: (a,b) => {
console.log(this.name,a,b);//this指向win
}
};
obj.func.call(obj,1,2);// 1 2
obj.func.apply(obj,[1,2]);
</script>
call 、 apply或 bind等方法给 this传值,箭头函数会忽略。箭头函数引用的是箭头函数在创建时设置的 this值。
例题2
<script>
let name = "zjk";
let o = {
name : "Jake",
sayName: function () {
console.log(this.name)
},
func: function () {
setTimeout( () => {
this.sayName()
},100);
}
};
o.func()//jake
</script>
7.call、apply和bind中的this
call、apply、bind 被称之为 this 的强绑定,用来改变函数执行时的this指向,目前所有关于它们的运用,都是基于这一点来进行的。
因为有call、apply和bind的存在this变得非常灵活 同时也减少了栈空间的使用
var abc = function (){console.log("我是:"+this.name) }
var obj1 = {name:"wc",say:abc}
var obj2 = {name:"xq",say:abc}
// 函数也是对象 对象是属性的无序集合 默认就有非常多的属性
// 其中有一个就是call
// call 的参数 对象
// 作用:
// 1)让abc中的this指向obj1 call改变this指向
// 2)让abc直接执行 调用
abc.call(obj1); // this==>obj1 abc()
abc.call(obj2); //
call、apply和bind的本质就是借 同理call、apply和bind也可以借用方法
<script>
// 数组中有一个翻转的方法
var arr = [1,2,3];
console.log(arr.reverse()); // [3, 2, 1]
// 目标:让字符串翻转
// 把字符串变成数组
var str = "123456789";
console.log(str.split("")); // ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
console.log(str.split("").reverse()); // ["9", "8", "7", "6", "5", "4", "3", "2", "1"]
console.log(str.split("").reverse().join()); // 9,8,7,6,5,4,3,2,1
console.log(str.split("").reverse().join("")); // 987654321
// 好办法
console.log(Array.prototype.reverse.call(str.split("")).join("")); // 987654321
</script>
call、apply和bind的区别
// call 改变了this指向 并 让函数执行 传递参数一个个传递
// apply 改变了this指向 并 让函数执行 传送参数必须是数组
// bind 改变了this指向 函数不执行
console.log(Math.max.call(arr, 2, 3, 3, 6, 9, 10, 2, 1));
console.log(Math.max.apply(arr, [2,3,3,6,9,10,2,1]));
// apply 和 call一模一样 唯一区别是传递参数的区别
拓展
8.在class中的this
在es6中,类,是 JavaScript 应用程序中非常重要的一个部分。类通常包含一个 constructor , this可以指向任何新创建的对象。
不过在作为方法时,如果该方法作为普通函数被调用, this也可以指向任何其他值。与方法一样,类也可能失去对接收器的跟踪。
class Hero {
constructor(heroName) {
this.heroName = heroName;
}
dialogue() {
console.log(`I am ${this.heroName}`)
}
}
const batman = new Hero("Batman");
batman.dialogue();
复制代码构造函数里的 this指向新创建的 类实例。当我们调用 batman.dialogue()时, dialogue()作为方法被调用, batman是它的接收器。
但是如果我们将 dialogue()方法的引用存储起来,并稍后将其作为函数调用,我们会丢失该方法的接收器,此时 this参数指向 undefined 。
const say = batman.dialogue;
say();
复制代码出现错误的原因是JavaScript 类是隐式的运行在严格模式下的。我们是在没有任何自动绑定的情况下调用 say()函数的。要解决这个问题,我们需要手动使用 bind()将 dialogue()函数与 batman绑定在一起。
const say = batman.dialogue.bind(batman);
say();