国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript 中一顆有毒的語法糖

Wuv1Up / 1507人閱讀

摘要:在中其實是一顆語法糖,但是這糖有毒。致命的地方在于它的指向往往不能直觀確定。希望下面可以一步步去掉有毒的糖衣。這樣理解可能有些極端,但是它可能有助于避免一些常見的錯誤。第三步一個傳遞參數(shù)更好的辦法仍存在兩個安全隱患。

在 JavaScript 中 this 其實是一顆語法糖,但是這糖有毒。this 致命的地方在于它的指向往往不能直觀確定。希望下面可以一步步去掉有毒的糖衣。

1 用 f.call(thisVal, ...args) 指定 this

調(diào)用函數(shù)的方式有三種,用 Function.prototype.call 調(diào)用可以指定 this:

定義 function f(...args){/*...*/}

調(diào)用 f.call(thisVal, ...args);

例一

function greet(){
  console.log("Hello, " + this);
}
// 手動指定 `greet` 中的 `this`:
greet.call("ngolin"); // Hello, ngolin

例二

function whoAreYou(){
  console.log("I"m " + this.name);
}
whoAreYou.call({name: "Jane"}); // I"m Jane
2 使用語法糖,this 自動指定

先接受函數(shù) f 的正確調(diào)用方式是 f.call(thisVal, ...args);, 然后就可以把 f(...args); 理解成語法糖。

但是不用 f.call(thisVal, ...args), this 怎樣動態(tài)指定?

一、函數(shù)(function)

// 1. 在非嚴(yán)格模式下:window
f(); // 解糖為 f.call(window);
// 2. 但在嚴(yán)格模式下:undefined
f(1, 2, 3); // 解糖為 f.call(undefined, 1, 2, 3);

一、方法(method)

// 無論是在嚴(yán)格還是非嚴(yán)格模式:
obj.m(1, 2, 3); // 解糖為 obj.m.call(obj, 1, 2, 3);
obj1.obj2.m(...args); // obj1.obj2.m.call(obj1.obj2, ...args);
obj1.obj2....objn.m(); // obj1.obj2....objn.m.call(obj1.obj2....objn);

通過上面的例子,分別演示了函數(shù) f(..args) 和方法 obj1.obj2....objn.m(..args) 怎樣自動指定 this.

嚴(yán)格區(qū)分函數(shù)(function)和方法(method)這兩個概念有利于清晰思考,因為它們在綁定 this 時發(fā)生的行為完全不一樣。同時函數(shù)和方法可以相互賦值(轉(zhuǎn)換),在賦值前后,唯一發(fā)生變化的是綁定 this 的行為(當(dāng)然這種變化在調(diào)用時才會體現(xiàn))。下面先看函數(shù)轉(zhuǎn)方法,再看方法轉(zhuǎn)函數(shù)。

3 函數(shù)轉(zhuǎn)方法

函數(shù)聲明(function f(){})和函數(shù)表達(dá)式(var f = function(){};)有一些微妙的區(qū)別,但是兩種方式在調(diào)用時綁定this行為完全一樣,下面在嚴(yán)格模式下以函數(shù)表達(dá)式為例:

var f = function(){
  console.log(this.name);
};

var obj1 = {
  name: "obj 1",
  getName: f;
};

var obj2 = {
  name: "obj 2",
  getName: f;
};

// 函數(shù) `f` 轉(zhuǎn)方法 `obj1.getName`
obj1.getName();// "obj 1" => obj1.getName.call(obj1)
// 不認(rèn)為函數(shù)轉(zhuǎn)方法
obj2.getName.call(obj1);// "obj 1"(不是 "obj 2")

將函數(shù)轉(zhuǎn)成方法通常不太容易出錯,因為起碼在方法中 this 能夠有效地指向一個對象。函數(shù)轉(zhuǎn)成方法是一個模糊的說法,實際上可以這樣理解:

JavaScript 不能定義一個函數(shù),也不能定義一個方法,是函數(shù)還是方法,要等到它執(zhí)行才能確定;當(dāng)把它當(dāng)成函數(shù)執(zhí)行,它就是函數(shù),當(dāng)把它當(dāng)成方法執(zhí)行,它就是方法。所以只能說執(zhí)行一個函數(shù)和執(zhí)行一個方法。

這樣理解可能有些極端,但是它可能有助于避免一些常見的錯誤。因為關(guān)系到 this 怎樣綁定,重要的是在哪里調(diào)用(比如在 obj1, obj2... 上調(diào)用)以及怎樣調(diào)用(比如以 f(), f.call()... 的方式),而不是在哪里定義。

但是,為了表達(dá)的方便,這里仍然會使用定義函數(shù)定義方法這兩種說法。

4 方法轉(zhuǎn)函數(shù)

將方法轉(zhuǎn)成函數(shù)比較容易出錯,比如:

var obj = {
  name: "obj",
  show: function(){
    console.log(this.name);
  }
};

var _show = obj.show;
_show(); // error!! => _show.call(undefined)

button.onClick = obj.show;

button.onClick(); // error!! => button.onClick.call(button)

(function(cb){
  cb(); // error!! =>cb.call(undefined)
})(obj.show);

當(dāng)一個對象的方法使用了 this 時,如果這個方法最后不是由這個對象調(diào)用(比如由其他框架調(diào)用),這個方法就可能會出錯。但是有一種技術(shù)可以將一個方法(或函數(shù))綁定(bind)在一個對象上,從而無論怎樣調(diào)用,它都能夠正常執(zhí)行。

5 把方法綁定(bind)在對象上

先看這個obj.getName的例子:

var obj = {
  getName: function(){
    return "ngolin";
  }
};

obj.getName(); // "ngolin"
obj.getName.call(undefined); // "ngolin"
obj.getName.call({name: "ngolin"}); // "ngolin"

var f = obj.getName;
f(); // "ngolin"

(function(cb){
  cb(); // "ngolin"
})(obj.getName);

上面的例子之所以可以成功是因為 obj.getName 根本沒有用到 this, 所以 this 指向什么對 obj.getName 都沒有影響。

這里有一種技術(shù)把使用 this 的方法轉(zhuǎn)成不使用 this 的方法,就是創(chuàng)建兩個閉包(即函數(shù)),第一個閉包將方法(method)和對象(obj)捕獲下來并返回第二個閉包,而第二個閉包用于調(diào)用并返回 obj.method.call(obj);. 下面一步步實現(xiàn)這種技術(shù):

第一步 最簡單的情況下:

function method(){
  obj.method.call(obj);
}
method(); // correct, :))

存在的缺陷:

只適合沒有參數(shù)和返回的 obj.method

存在兩個安全隱患:
1 后續(xù)改變 obj.method,比如 obj.method = null;
2 后續(xù)改變 obj,比如 obj = null

第二步 在方法有參數(shù)有返回的情況下:

function method(a, b){
  return obj.method.call(obj, a, b);
}
method(a, b); // correct, :))

存在的缺陷:

只適合兩個參數(shù)的 obj.method

存在兩個安全隱患,同上。

第三步 一個傳遞參數(shù)更好的辦法:

function method(){
  return obj.method.apply(obj, arguments);
}
method(a, b); // correct, :))

仍存在兩個安全隱患。

第四步 更加安全的方式:

var method = (function(){
  return function(){
    return obj.method.apply(obj, arguments);
  };
})(obj.method, obj);

method(a, b); // correct, :))

第五步 抽象出一個函數(shù),用于將方法綁定到對象上:

function bind(method, obj){
  return function(){
    return method.apply(obj, arguments);
  };
}

var obj = {
  name: "ngolin",
  getName: function(){
    return this.name;
  }
};

var method = bind(obj.getName, obj);
method(); // "ngolin"
6 Function.prototype.bind

這種方法很常見,后來 ECMAScript 5 就增加了 Function.prototype.bind, 比如:

var binded = function(){
  return this.name;
}.bind({name: "ngolin"});

binded(); // "ngolin"

具體來說,Function.prototype.bind 這樣工作:

var bindedMethod = obj.method.bind(obj);
// 相當(dāng)于:
var bindedMethod = (function(){
  return function(){
    return obj.method.apply(obj, arguments);
  };
})(obj.method, obj);

更多使用 Function.prototype.bind 的例子:

var f = obj.method.bind(obj);

button.onClick = obj.method.bind(obj);

document.addEventListener("click", obj.method.bind(obj));
7 常見問題及容易出錯的地方

在定義對象時有沒有 this?

obj = {
  firstName: "First",
  lastName: "Last",
  // `fullName` 可以得到預(yù)期結(jié)果嗎?
  fullName: this.firstName + this.lastName
}

// 或者:

function makePoint(article){
  if(article.length <= 144) return article;
  return article.substr(0, 141) + "...";
}
obj = {
  fulltext: "...a long article go here...",
  // `abstract` 呢?
  abstract: makePoint(this.fulltext)
}

在方法內(nèi)的 this 都是同一對象嗎?

obj = {
  count: 3,
  field: "field",
  method: function(){
    function repeat(){
      if(this.count > 100){
        return this.field.repeat(this.count % 100);
      }
      this.field.repeat(this.count);
    }.bind(this);
    // 這個呢?
    return repeat();
  }
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/89453.html

相關(guān)文章

  • 理解JavaScriptcall,apply和bind方法

    摘要:輸出的作用與和一樣,都是可以改變函數(shù)運行時上下文,區(qū)別是和在調(diào)用函數(shù)之后會立即執(zhí)行,而方法調(diào)用并改變函數(shù)運行時上下文后,返回一個新的函數(shù),供我們需要時再調(diào)用。 前言 js中的call(), apply()和bind()是Function.prototype下的方法,都是用于改變函數(shù)運行時上下文,最終的返回值是你調(diào)用的方法的返回值,若該方法沒有返回值,則返回undefined。這幾個方法...

    chaosx110 評論0 收藏0
  • What's New in JavaScript

    摘要:在和中都保留了數(shù)組的強(qiáng)引用,所以在中簡單的清除變量內(nèi)存并沒有得到釋放,因為還存在引用計數(shù)。而在中,它的鍵是弱引用,不計入引用計數(shù)中,所以當(dāng)被清除之后,數(shù)組會因為引用計數(shù)為而被回收掉。其實我們主要注意的引用是不計引用計數(shù)的,就好理解了。 showImg(https://segmentfault.com/img/remote/1460000019147368?w=900&h=383); 前...

    cgh1999520 評論0 收藏0
  • react本質(zhì):JSX如何轉(zhuǎn)化為javascript

    摘要:中基本都使用來開發(fā),但其實是的一種語法糖。但是我們必須知道,本質(zhì)上就是在編譯的時候,會由將轉(zhuǎn)化為。比如生成了比如生成了解的本質(zhì),只需要記住本質(zhì)就是附錄提供的一個在線轉(zhuǎn)換為的地址 react中基本都使用JSX來開發(fā),但JSX其實是javascript的一種語法糖。 什么是語法糖? 語法糖就是提供了一種全新的方式書寫代碼,但是其實現(xiàn)原理與之前的寫法相同。語法糖可以說是廣泛存在于各種計算機(jī)...

    ChanceWong 評論0 收藏0
  • leetcode135. Candy

    摘要:題目要求假設(shè)有個孩子站成一排,每個孩子擁有一個評估值。我們可以觀察到,每次最遠(yuǎn)只需要額外分發(fā)到距離當(dāng)前最近的評分最高的那個孩子。因為他的糖果數(shù)量的增加并不會影響到之前孩子。當(dāng)有多個最近評分最高的孩子時,則選擇最后一個。 題目要求 There are N children standing in a line. Each child is assigned a rating value....

    shmily 評論0 收藏0
  • [Leetcode] Candy 分

    摘要:貪心法復(fù)雜度時間空間思路典型的貪心法,如果一個孩子比另一個孩子的分高,我們只多給塊糖。我們可以先從左往右遍歷,確保每個孩子根他左邊的孩子相比,如果分高,則糖要多個,如果分比左邊低,就只給一顆。 Candy There are N children standing in a line. Each child is assigned a rating value. You are gi...

    張憲坤 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<