JavaScript深度學習

來源:互聯網
上載者:User

標籤:

1、Javascript範圍原理

var name = ‘laruence‘;
    function echo() {
         alert(name);   //laruence
         var name = ‘eve‘;
         alert(name);   //eve
         alert(age);    //指令碼錯誤
    }
     
    echo();

運行結果是什麼呢?

上面的問題, 我相信會有很多人會認為應該像注釋那樣的。

執行了一次之後發現第一次alert出來確是undefined。為什麼呢?

JS權威指南中有一句很精闢的描述: ”JavaScript中的函數運行在它們被定義的範圍裡,而不是它們被執行的範圍裡.”

其實在JS中:”一切皆是對象, 函數也是”。

在一個函數被定義的時候, 會將它定義時刻的scope chain連結到這個函數對象的[[scope]]屬性.
在一個函數對象被調用的時候,會建立一個使用中的物件(也就是一個對象), 然後對於每一個函數的形參,都命名為該使用中的物件的命名屬性, 然後將這個使用中的物件做為此時的範圍鏈(scope chain)最前端, 並將這個函數對象的[[scope]]加入到scope chain中.

看個例子:

  1.      var func = function(lps, rps){
  2.           var name = ‘laruence‘;
  3.           ........
  4.      }
  5.      func();

在調用func的時候, 會建立一個使用中的物件(假設為aObj, 由JS引擎先行編譯時刻建立, 後面會介紹),並建立arguments屬性, 然後會給這個對象添加倆個命名屬性aObj.lps, aObj.rps;

對於每一個在這個函數中申明的局部變數和函數定義, 都作為該使用中的物件的同名命名屬性.

有了上面的範圍鏈, 在發生標識符解析的時候, 就會逆向查詢當前scope chain列表的每一個使用中的物件的屬性,如果找到同名的就返回。找不到,那就是這個標識符沒有被定義。

注意到, 因為函數對象的[[scope]]屬性是在定義一個函數的時候決定的, 而非調用的時候, 所以如下面的例子:

  1. function factory() {
  2.      var name = ‘laruence‘;
  3.      var intro = function(){
  4.           alert(‘I am ‘ + name);
  5.      }
  6.      return intro;
  7. }
  8.  
  9. function app(para){
  10.      var name = para;
  11.      var func = factory();
  12.      func();
  13. }
  14.  
  15. app(‘eve‘);

當調用app的時候, scope chain是由: {window使用中的物件(全域)}->{app的使用中的物件} 組成.

在剛進入app函數體時, app的使用中的物件有一個arguments屬性, 倆個值為undefined的屬性: name和func. 和一個值為’eve’的屬性para;

此時的scope chain如下:

    1. [[scope chain]] = [
    2. {
    3.      para : ‘eve‘,
    4.      name : undefined,
    5.      func : undefined,
    6.      arguments : []
    7. }, {
    8.      window call object
    9. }

    注意到, 此時的範圍鏈中, 並不包含app的使用中的物件.

    在定義intro函數的時候, intro函數的[[scope]]為:

    1. [[scope chain]] = [
    2. {
    3.      name : ‘laruence‘,
    4.      intor : undefined
    5. }, {
    6.      window call object
    7. }
    8. ]

從factory函數返回以後,在app體內調用intor的時候, 發生了標識符解析, 而此時的sope chain是:

  1. [[scope chain]] = [
  2. {
  3.      intro call object
  4. }, {
  5.      name : ‘laruence‘,
  6.      intor : undefined
  7. }, {
  8.      window call object
  9. }
  10. ]

因為scope chain中,並不包含factory使用中的物件. 所以, name標識符解析的結果應該是factory使用中的物件中的name屬性, 也就是’laruence’.

所以運行結果是:

  I am laruence

現在, 大家對”JavaScript中的函數運行在它們被定義的範圍裡,而不是它們被執行的範圍裡.”這句話, 應該有了個全面的認識了吧?

2、原型和原型鏈

 

原型使用方式1

var Calculator = function (decimalDigits, tax) {

  this.decimalDigits = decimalDigits;

  this.tax = tax;

};

然後,通過給Calculator對象的prototype屬性賦值對象字面量來設定Calculator對象的原型。

Calculator.prototype = {

  add: function (x, y) {

    return x + y;

  },

  subtract: function (x, y) {

    return x - y;

   }

};

//alert((new Calculator()).add(1, 3));

這樣,我們就可以new Calculator對象以後,就可以調用add方法來計算結果了。

原型使用方式2

第二種方式是,在賦值原型prototype的時候使用function立即執行的運算式來賦值,即如下格式:

Calculator.prototype = function () { } ();

它的好處在前面的文章裡已經知道了,就是可以封裝私人的function,通過return的形式暴露出簡單的使用名稱,以達到public/private的效果,修改後的代碼如下

Calculator.prototype = function () {

  add = function (x, y) {

    return x + y;

  },

  subtract = function (x, y) {

    return x - y;

  }

  return {

    add: add,

    subtract: subtract

  }

} ();

//alert((new Calculator()).add(11, 3));

我們再來說一下如何分布來設定原型的每個屬性吧。

var BaseCalculator = function () {

  //為每個執行個體都聲明一個小數位元

  this.decimalDigits = 2;

};

//使用原型給BaseCalculator擴充2個對象方法

BaseCalculator.prototype.add = function (x, y) {

  return x + y;

};

BaseCalculator.prototype.subtract = function (x, y) {

  return x - y;

};

建立完上述代碼以後,我們來開始:

var Calculator = function () {

  //為每個執行個體都聲明一個數字

  this.tax = 5;

};

Calculator.prototype = new BaseCalculator();  

var calc = new Calculator();
alert(calc.add(1, 1));
//BaseCalculator 裡聲明的decimalDigits屬性,在 Calculator裡是可以訪問到的
alert(calc.decimalDigits);

function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// 設定Bar的prototype屬性為Foo的執行個體對象
Bar.prototype = new Foo();
Bar.prototype.foo = ‘Hello World‘;

// 修正Bar.prototype.constructor為Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 建立Bar的一個新執行個體

// 原型鏈
test [Bar的執行個體]
    Bar.prototype [Foo的執行個體]
        { foo: ‘Hello World‘ }
        Foo.prototype
            {method: ...};
            Object.prototype
                {toString: ... /* etc. */};
3、閉包的範例

例子1:閉包中局部變數是引用而非拷貝

function say667() {
    // Local variable that ends up within closure
    var num = 666;
    var sayAlert = function() { alert(num); }
    num++;
    return sayAlert;
}
 
var sayAlert = say667();
sayAlert()

因此執行結果應該彈出的667而非666。

例子2:多個函數綁定同一個閉包,因為他們定義在同一個函數內。

function setupSomeGlobals() {
    // Local variable that ends up within closure
    var num = 666;
    // Store some references to functions as global variables
    gAlertNumber = function() { alert(num); }
    gIncreaseNumber = function() { num++; }
    gSetNumber = function(x) { num = x; }
}
setupSomeGolbals(); // 為三個全域變數賦值
gAlertNumber(); //666
gIncreaseNumber();
gAlertNumber(); // 667
gSetNumber(12);//
gAlertNumber();//12

例子3:當在一個迴圈中賦值函數時,這些函數將綁定同樣的閉包

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = ‘item‘ + list[i];
        result.push( function() {alert(item + ‘ ‘ + list[i])} );
    }
    return result;
}
 
function testList() {
    var fnlist = buildList([1,2,3]);
    // using j only to help prevent confusion - could use i
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

testList的執行結果是彈出item3 undefined視窗三次,因為這三個函數綁定了同一個閉包,而且item的值為最後計算的結果,但是當i跳出迴圈時i值為4,所以list[4]的結果為undefined.

例子4:外部函數所有局部變數都在閉包內,即使這個變數聲明在內建函式定義之後。

function sayAlice() {
    var sayAlert = function() { alert(alice); }
    // Local variable that ends up within closure
    var alice = ‘Hello Alice‘;
    return sayAlert;
}
var helloAlice=sayAlice();
helloAlice();

執行結果是彈出”Hello Alice”的視窗。即使局部變數聲明在函數sayAlert之後,局部變數仍然可以被訪問到。

例子5:每次函數調用的時候建立一個新的閉包

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.push(num);
        alert(‘num: ‘ + num +
        ‘\nanArray ‘ + anArray.toString() +
        ‘\nref.someVar ‘ + ref.someVar);
    }
}
closure1=newClosure(40,{someVar:‘closure 1‘});
closure2=newClosure(1000,{someVar:‘closure 2‘});
 
closure1(5); // num:45 anArray[1,2,3,45] ref:‘someVar closure1‘
closure2(-10);// num:990 anArray[1,2,3,990] ref:‘someVar closure2‘

JavaScript深度學習

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.