源:Loong Panda
一、不同环境下的this的指向:
1、this的指向是在函数运行时确定下来的,而不是创建的时候,谁调用就指向谁。
2、ES5中普通函数this指向window,ES6中普通函数this指向为undefined;
3、事件的this指向,为事件源本身
4、定时器的this指向,为window.
5、构造函数内部的this指向new出来的这个对象
注: 严格模式下,普通函数和全局环境的this为undefined
consolog.log(this) // window
function test() { console.log(111, this) // window } test()
注: 'use strict' 严格模式下
consolog.log(this) // undefined
function test() { console.log(111, this) // undefined } test()
let obj3={
say() {
console.log(6666, this) // obj3
return function () {
console.log(7777, this) // window
}
}
}
obj3.say()()
function Person(e) {
this.name=e console.log(5555, this)
}
let a=new Person('李四1') // Person { name: '李四1'}
let b=new Person('李四2') // Person { name: '李四2'}
let obj2={
name: '张三',
say: function () {
console.log(333, this) // obj2
setTimeout(function () {
console.log(444, this) // window
}, 1000)
}
}
obj2.say()
let obj3={
say() {
console.log(6666, this) // obj3
return ()=> {
console.log(7777, this) // obj3
}
}
}
obj3.say()()
二、改变this指向的方法:
1、箭头函数 -----> 只适合现代浏览器;
2、bind() -----> 第一个是参数是指向谁,第二个以后的参数是自定义的参数,需要单独调用才执行,如: bind(obj)()
3、call(xx, arg1, arg2) -----> 第一个是参数是指向谁,第二个以后的参数是自定义的参数
4、apply(xx, [arg1, arg2]) -----> 第一个是参数是指向谁,第二个的参数是自定义的参数,数据类型必须是数组
在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象。一般在编译期确定下来,或称为编译期绑定。而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,带来灵活性的同时,也为初学者带来不少困惑。本文仅就这一问题展开讨论,阅罢本文,读者若能正确回答 JavaScript 中的 What 's this 问题,那就会觉得花费这么多功夫,撰写这样一篇文章是值得的。
在 Java 中定义类经常会使用 this 关键字,多数情况下是为了避免命名冲突,比如在下面例子的中,定义一个 Point 类,很自然的,大家会使用 x,y 为其属性或成员变量命名,在构造函数中,使用 x,y 为参数命名,相比其他的名字,比如 a,b,也更有意义。这时候就需要使用 this 来避免命名上的冲突。另一种情况是为了方便的调用其他构造函数,比如定义在 x 轴上的点,其 x 值默认为 0,使用时只要提供 y 值就可以了,我们可以为此定义一个只需传入一个参数的构造函数。无论哪种情况,this 的含义是一样的,均指当前对象。
清单 1. Point.java
由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。下面我们将按照调用方式的不同,分别讨论 this 的含义。
在 JavaScript 中,函数也是对象,因此函数可以作为一个对象的属性,此时该函数被称为该对象的方法,在使用这种调用方式时,this 被自然绑定到该对象。
清单 2. point.js
函数也可以直接被调用,此时 this 绑定到全局对象。在浏览器中,window 就是该全局对象。比如下面的例子:函数被调用时,this 被绑定到全局对象,接下来执行赋值语句,相当于隐式的声明了一个全局变量,这显然不是调用者希望的。
清单 3. nonsense.js
对于内部函数,即声明在另外一个函数体内的函数,这种绑定到全局对象的方式会产生另外一个问题。我们仍然以前面提到的 point 对象为例,这次我们希望在 moveTo 方法内定义两个函数,分别将 x,y 坐标进行平移。结果可能出乎大家意料,不仅 point 对象没有移动,反而多出两个全局变量 x,y。
清单 4. point.js
这属于 JavaScript 的设计缺陷,正确的设计方式是内部函数的 this 应该绑定到其外层函数对应的对象上,为了规避这一设计缺陷,聪明的 JavaScript 程序员想出了变量替代的方法,约定俗成,该变量一般被命名为 that。
清单 5. point2.js
JavaScript 支持面向对象式编程,与主流的面向对象式编程语言不同,JavaScript 并没有类(class)的概念,而是使用基于原型(prototype)的继承方式。相应的,JavaScript 中的构造函数也很特殊,如果不使用 new 调用,则和普通函数一样。作为又一项约定俗成的准则,构造函数以大写字母开头,提醒调用者使用正确的方式调用。如果调用正确,this 绑定到新创建的对象上。
清单 6. Point.js
让我们再一次重申,在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。很多 JavaScript 中的技巧以及类库都用到了该方法。让我们看一个具体的例子:
清单 7. Point2.js
在上面的例子中,我们使用构造函数生成了一个对象 p1,该对象同时具有 moveTo 方法;使用对象字面量创建了另一个对象 p2,我们看到使用 apply 可以将 p1 的方法应用到 p2 上,这时候 this 也被绑定到对象 p2 上。另一个方法 call 也具备同样功能,不同的是最后的参数不是作为一个数组统一传入,而是分开传入的。
如果像作者一样,大家也觉得上述四种方式不方便记忆,过一段时间后,又搞不明白 this 究竟指什么。那么我向大家推荐 Yehuda Katz 的这篇文章:( http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/)。在这篇文章里,Yehuda Katz 将 apply 或 call 方式作为函数调用的基本方式,其他几种方式都是在这一基础上的演变,或称之为语法糖。Yehuda Katz 强调了函数调用时 this 绑定的过程,不管函数以何种方式调用,均需完成这一绑定过程,不同的是,作为函数调用时,this 绑定到全局对象;作为方法调用时,this 绑定到该方法所属的对象。
通过上面的描述,如果大家已经能明确区分各种情况下 this 的含义,这篇文章的目标就已经完成了。如果大家的好奇心再强一点,想知道为什么 this 在 JavaScript 中的含义如此丰富,那就得继续阅读下面的内容了。作者需要提前告知大家,下面的内容会比前面稍显枯燥,如果只想明白 this 的含义,阅读到此已经足够了。如果大家不嫌枯燥,非要探寻其中究竟,那就一起迈入下一节吧。
JavaScript 中的函数既可以被当作普通函数执行,也可以作为对象的方法执行,这是导致 this 含义如此丰富的主要原因。一个函数被执行时,会创建一个执行环境(ExecutionContext),函数的所有的行为均发生在此执行环境中,构建该执行环境时,JavaScript 首先会创建 arguments变量,其中包含调用函数时传入的参数。接下来创建作用域链。然后初始化变量,首先初始化函数的形参表,值为 arguments变量中对应的值,如果 arguments变量中没有对应值,则该形参初始化为 undefined。如果该函数中含有内部函数,则初始化这些内部函数。如果没有,继续初始化该函数内定义的局部变量,需要注意的是此时这些变量初始化为 undefined,其赋值操作在执行环境(ExecutionContext)创建成功后,函数执行时才会执行,这点对于我们理解 JavaScript 中的变量作用域非常重要,鉴于篇幅,我们先不在这里讨论这个话题。最后为 this变量赋值,如前所述,会根据函数调用方式的不同,赋给 this全局对象,当前对象等。至此函数的执行环境(ExecutionContext)创建成功,函数开始逐行执行,所需变量均从之前构建好的执行环境(ExecutionContext)中读取。
有了前面对于函数执行环境的描述,我们来看看 this 在 JavaScript 中经常被误用的一种情况:回调函数。JavaScript 支持函数式编程,函数属于一级对象,可以作为参数被传递。请看下面的例子 myObject.handler 作为回调函数,会在 onclick 事件被触发时调用,但此时,该函数已经在另外一个执行环境(ExecutionContext)中执行了,this 自然也不会绑定到 myObject 对象上。
清单 8. callback.js
button.onclick=myObject.handler;
这是 JavaScript 新手们经常犯的一个错误,为了避免这种错误,许多 JavaScript 框架都提供了手动绑定 this 的方法。比如 Dojo 就提供了 lang.hitch,该方法接受一个对象和函数作为参数,返回一个新函数,执行时 this 绑定到传入的对象上。使用 Dojo,可以将上面的例子改为:
清单 9. Callback2.js
button.onclick=lang.hitch(myObject, myObject.handler);
在新版的 JavaScript 中,已经提供了内置的 bind 方法供大家使用。
JavaScript 中的 eval 方法可以将字符串转换为 JavaScript 代码,使用 eval 方法时,this 指向哪里呢?答案很简单,看谁在调用 eval 方法,调用者的执行环境(ExecutionContext)中的 this 就被 eval 方法继承下来了。简单看个eval示例:
本文介绍了 JavaScript 中的 this 关键字在各种情况下的含义,虽然这只是 JavaScript 中一个很小的概念,但借此我们可以深入了解 JavaScript 中函数的执行环境,而这是理解闭包等其他概念的基础。掌握了这些概念,才能充分发挥 JavaScript 的特点,才会发现 JavaScript 语言特性的强大。
原文:https://dev.to/bhagatparwinder/arrow-function-basics-34cm
箭头函数是在 ES6 引入的,相对于函数表达式来说是一种更简洁的方式。
箭头函数名称的来源是因为使用了 =>。
const functionName = (arg1, arg2, ... argN) => {
return value;
}
const multiply = (a, b) => {
return a * b;
}
console.log(multiply(7, 8)); // 56
console.log(multiply(3, 2)); // 6
const square = x => {
return x * x;
}
console.log(square(2)); // 4
console.log(square(7)); // 49
这个情形的唯一陷阱是当只有一个参数且需要解构时:
const foo = ({name = "New User"}) => name;
console.log(foo({})); // New User
console.log(foo({name: "Parwinder"})); // Parwinder
const greeting = () => {
return "Hello World!";
}
console.log(greeting()); // Hello World!
const greeting = () => "Hello World!";
console.log(greeting()); // Hello World
现在我们知道了所有的关键特点,让我们来重写获取正方形的面积:
const square = x => x * x;
console.log(square(4)); // 16
JavaScript 中的 this 关键字是执行上下文的一个属性,它可能是全局的、函数内的或者是 eval 中的。对于普通的函数,this 会根据调用它方式不同而变化。
我们使用这种行为已经很久了,以至于大多数JavaScript开发者都已经习惯了。
function foo() {
return this;
};
console.log(foo()); // window object in a browser, global object for node execution
function Order(main, side, dessert) {
this.main = main;
this.side = side;
this.dessert = dessert;
this.order = function () {
return `I will have ${this.main} with ${this.side} and finish off with a ${this.dessert}`;
}
}
const newOrder = new Order("sushi", "soup", "yogurt");
console.log(newOrder.order());
// I will have sushi with soup and finish off with a yogurt
const myObject = {
main: "butter chicken",
side: "rice",
dessert: "ice cream",
order: function () {
return `I will have ${this.main} with ${this.side} and finish off with ${this.dessert}`;
}
}
console.log(myObject.order());
// I will have butter chicken with rice and finish off with ice cream
上面的例子中,this 指向 myObject,可以获取它上面的属性。
"use strict";
function foo() {
return this;
};
console.log(foo() === undefined); // true
*请认真填写需求信息,我们会在24小时内与您取得联系。