资讯专栏INFORMATION COLUMN

前端之ECMAScript 实战:this的用法

kun_jian / 1327人阅读

摘要:在等面向对象的语言中,关键字的含义是明确且具体的,即指代当前对象。一般在编译期确定下来,或称为编译期绑定。无论是用还是这种方式,都取决于传入该函数的对象。使用方法绑定使用在创建时绑定指针。如果是行内的事件监听者,指针会被设置为其所在的元素

this

在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象。一般在编译期确定下来,或称为编译期绑定。而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这就导致 JavaScript 中的 this 关键字有能力具备多重含义,变得有点随意。而在ES6中又引入了Arrow Function以及Class,它们对于this指针也带来了一定的影响。

Global Context(全局上下文)

在任何函数之外的全局环境中,不管在不在strict模式中,this指针往往指向一个全局变量。

console.log(this.document === document); // true

// In web browsers, the window object is also the global object:
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37
Function Context(函数上下文) 简单调用

在某个函数中,this的值取决于该函数的调用者。无论是用hello("world”)还是call这种方式,都取决于传入该函数的对象。不过,在ES5的严格或者不严格模式下,同样的调用方式会有不同的结果。

function hello(thing) {  
  console.log("Hello " + thing);
}

// this:
hello("world")

// desugars to:
hello.call(window, "world");  

而如果是strict模式下:

// this:
hello("world")

// desugars to:
hello.call(undefined, "world");  
对象方法

另一种常用的调用函数的方法就是在object中调用:

function hello(thing) {  
  console.log(this + " says hello " + thing);
}

person = { name: "Brendan Eich" }  
person.hello = hello;

person.hello("world") // still desugars to person.hello.call(person, "world") [object Object] says hello world

hello("world") // "[object DOMWindow]world"  
bind

很多时候,需要为某个函数指定一个固定的this对象,最简单的方式即是使用闭包来获取一个不变的this对象。

var person = {  
  name: "Brendan Eich",
  hello: function(thing) {
    console.log(this.name + " says hello " + thing);
  }
}

var boundHello = function(thing) { return person.hello.call(person, thing); }

boundHello("world");  

不过,这种方式仍然存在着一定的问题,ES5为Function对象引入了一个新的bind方法来解决这个问题:

var boundHello = person.hello.bind(person);  
boundHello("world") // "Brendan Eich says hello world"  

这种方式在设置回调函数中的this指针的时候会起到很大的作用,特别是在React中,为了保证指针的稳定性,往往需要为内置方法设置bind。

var person = {  
  name: "Alex Russell",
  hello: function() { console.log(this.name + " says hello world"); }
}

$("#some-div").click(person.hello.bind(person));

// when the div is clicked, "Alex Russell says hello world" is printed
apply/call

在 JavaScript 中函数也是对象,对象则有方法,apply 和 call 就是函数对象的方法。这两个方法异常强大,他们允许切换函数执行的上下文环境(context),即 this 绑定的对象。很多 JavaScript 中的技巧以及类库都用到了该方法。让我们看一个具体的例子:

function Point(x, y){ 
    this.x = x; 
    this.y = y; 
    this.moveTo = function(x, y){ 
        this.x = x; 
        this.y = y; 
    } 
 } 

 var p1 = new Point(0, 0); 
 var p2 = {x: 0, y: 0}; 
 p1.moveTo(1, 1); 
 p1.moveTo.apply(p2, [10, 10]);
array.forEach

在这样一个回调函数中,回调函数的this指针是由调用者决定的,完整的forEach声明如下:array.forEach(callback[, thisArg]),这个传入的thisArg即是回调的调用者。

var o={
   v:"hello",
   p:["a1","a2"],
   f:function f(){
      this.p.forEach(function (item){
           console.log(this.v+" "+item);
      });
   }
}

o.f();
//undefined a1
//undefined a2
Arrow Function

Arrow Function是ES6新增的特性,很类似于Java或者C#中的Lambda表达式。Arrow函数中的this指针在创建时就被绑定到了闭合的作用域内,不会收到new、bind、call以及apply这些方法的影响。

var o = {
  traditionalFunc: function () {
    // Normal function, bound as expected
    console.log("traditionalFunc this === o?", this === o);
  },
  arrowFunc: () => {
    // Arrow function, bound to scope where it"s created
    console.log("arrowFunc this === o?", this === o);
    console.log("arrowFunc this === window?", this === window);
  }
};

o.traditionalFunc();
// traditionalFunc this === o? true

o.arrowFunc();
// arrowFunc this === o? false
// arrowFunc this === window? true

上述代码中的arrowFunc隶属于o对象,但是在window的作用域中被创建,因此,最终arrow函数中的this指针的值等于window对象。ES5中的对于this的控制已然非常复杂,特别是在处理异步代码中如何传入合适的this对象也是一件麻烦事,如下文所示:

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
    callback(param);
  }, 1);
};

// With a traditional function if we don"t control 
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
    // Here we pass `o` into the async function, 
    // expecting it back as `param`
    asyncFunction(o, function (param) {
      // We made a mistake of thinking `this` is 
      // the instance of `o`.
      console.log("param === this?", param === this);
    });
  }
};

o.doSomething(); // param === this? false

为了绑定上述代码中的this指针,一般来说有三个办法:

为this指针创建一个固定的引用。

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
    callback(param);
  }, 1);
};

// Define a reference to `this` outside of the callback, 
// but within the callback"s lexical scope
var o = {
  doSomething: function () {
    var self = this;
    // Here we pass `o` into the async function, 
    // expecting it back as `param`
    asyncFunction(o, function (param) {
      console.log("param === this?", param === self);
    });
  }
};

o.doSomething(); // param === this? true

使用bind方法绑定this

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
    callback(param);
  }, 1);
};

// Here we control the context of the callback using
// `bind` ensuring `this` is correct
var o = {
  doSomething: function () {
    // Here we pass `o` into the async function, 
    // expecting it back as `param`
    asyncFunction(o, function (param) {
      console.log("param === this?", param === this);
    }.bind(this));
  }
};

o.doSomething(); // param === this? true

使用Arrow Function在创建时绑定this指针。

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
    callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
    // Here we pass `o` into the async function, 
    // expecting it back as `param`. 
    //
    // Because this arrow function is created within  
    // the scope of `doSomething` it is bound to this 
    // lexical scope.
    asyncFunction(o, (param) => {
      console.log("param === this?", param === this);
    });
  }
};

o.doSomething(); // param === this? true
DOM Event handler(Dom事件)

当某个函数作为事件监听器时,它的this值往往被设置为它的调用者。

// When called as a listener, turns the related element blue
function bluify(e){
  // Always true
  console.log(this === e.currentTarget); 
  // true when currentTarget and target are the same object
  console.log(this === e.target);
  this.style.backgroundColor = "#A5D9F3";
}

// Get a list of every element in the document
var elements = document.getElementsByTagName("*");

// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for(var i=0 ; i

如果是行内的事件监听者,this指针会被设置为其所在的DOM元素:

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86029.html

相关文章

  • ECMASCRIPT 6 实战 扩展运算符

    摘要:扩展运算符是以下简称中又一非常好用的实战技术它的写法只需要三个点作用则顾名思义用来展开你想要使用的任意变量本质上是对所有拥有迭代器接口的对象进行迭代。 扩展运算符(spreading)是 ECMASCRIPT 6(以下简称ES 6) 中又一非常好用的实战技术, 它的写法只需要三个点(...),作用则顾名思义,用来展开你想要使用的任意变量,本质上是对所有拥有迭代器接口(Iterator)...

    habren 评论0 收藏0
  • ECMASCRIPT 6 实战 解构赋值

    摘要:相信解构赋值自以下简称面世以来被大家快速地熟悉并运用到实际开发中了这是一种能有效减少代码量,使代码逻辑更简单优雅的技术下面我们就再来回顾总结一下解构赋值的种种用法吧基本用法从对象解构假设有一个对象,它的结构为以对称的形式从从边的对象中匹配与 相信解构赋值(Destructuring)自 ECMASCRIPT 6(以下简称 ES 6) 面世以来被大家快速地熟悉并运用到实际开发中了, 这是...

    yangrd 评论0 收藏0
  • 前端培训-初级阶段(13) - 正则表达式

    摘要:前端培训初级阶段语法变量值类型运算符语句前端培训初级阶段内置对象函数前端培训初级阶段类模块继承基础内容知识我们会用到。 前端最基础的就是 HTML+CSS+Javascript。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。 该文为前端培训-初级阶段(1...

    suemi 评论0 收藏0
  • 根治JavaScript中this-ECMAScript规范解读

    摘要:执行函数调用规范中的第一步是一个明显的赋值语句,我们查看规范,赋值语句会发生什么可以看出简单赋值语句返回的是对等于号右边进行之后的结果,上一节讲了,执行过就会返回的类型,此处会返回也就是一个类型。 前言 this是JavaScript中的著名月经题,每隔一段时间就有人翻出了拿各种奇怪的问题出来讨论,每次都会引发一堆口水之争。从搜索引擎搜了一下现在的比较热门的关于this的用法,如:Ja...

    notebin 评论0 收藏0
  • JavaScript - 收藏集 - 掘金

    摘要:插件开发前端掘金作者原文地址译者插件是为应用添加全局功能的一种强大而且简单的方式。提供了与使用掌控异步前端掘金教你使用在行代码内优雅的实现文件分片断点续传。 Vue.js 插件开发 - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins译者:jeneser Vue.js插件是为应用添加全局功能的一种强大而且简单的方式。插....

    izhuhaodev 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<