JavaScript簡(jiǎn)要

注意


  • 聲明提前(hoisting)
var scope = "global";
function f() {
    console.log(scope);        // -> undefined, 該作用域中scope已定義,但在這個(gè)地方還未賦值
    var scope = "local";
    console.log(scope);        // -> local
}
  • null和undefined不能包含屬性
  • with語(yǔ)句
o = {"x" : 0};
with(o) x = 1;        // o.x = 1
with(o) y = 2;        // 定義了全局變量y
// 即with語(yǔ)句提供訪問對(duì)象屬性的簡(jiǎn)便方法,但并不創(chuàng)建對(duì)象屬性
  • 函數(shù)的call方法
f = (function (x) {
    return this.a + x;
})
(function () {
    this.a = 20;
    f.call(this, 30);      // 50
    f.apply(this, [22])    // 42
} ())

JavaScript - The Good Parts

對(duì)象


JavaScript類型有數(shù)字,字符串,布爾(true/false), null和undefined. 所有其它值均為對(duì)象。

Object Literals

  • 空對(duì)象: {}
  • 非空對(duì)象為大括號(hào)擴(kuò)住的鍵值對(duì): {name : value, ...}
  • 值value部分可以是Object Literal
  • 鍵name部分可以是任意字符串, 若name是一個(gè)合法的JavaScript標(biāo)志符且不是保留字,則引號(hào)可以省略
  • 原型為Object.prototype

取值

  • obj["name"]
  • obj.name

更新值

  • obj["name"] = value
  • obj.name = value

引用

  • 對(duì)象通過引用傳遞

原型

每個(gè)對(duì)象關(guān)聯(lián)到一個(gè)原型對(duì)象,并且它可以從該對(duì)象繼承屬性. 所有創(chuàng)建自O(shè)bject Literal的對(duì)象均關(guān)聯(lián)到Object.prototype.

當(dāng)創(chuàng)建一個(gè)對(duì)象時(shí),你可以選擇作為它原型的對(duì)象.

// create方法接收一個(gè)對(duì)象,并以改對(duì)象為原型創(chuàng)建一個(gè)新的對(duì)象
if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

原型關(guān)系只在取值操作中起作用. 當(dāng)試圖獲取對(duì)象的某個(gè)不存在的屬性,JavaScript轉(zhuǎn)而從該對(duì)象的原型獲取該屬性,若仍不存在,繼續(xù)從原型的原型獲取,以此類推,若一直到Object.prototype都沒有該屬性,則返回undefined

反射

typeof "hello"            // 'string'
typeof 1                  // 'number'
typeof {}                 // 'object'
typeof undefined          // 'undefined'
typeof ''.toString        // 'function'

typeof 會(huì)沿著原型鏈查找屬性,若不希望這樣可以使用hasOwnProperty屬性

枚舉

for (var in aobject) {...}會(huì)枚舉對(duì)象aobject的所有屬性,包括函數(shù)和原型屬性,且對(duì)屬性的枚舉順序不定。當(dāng)然也可以通過將想要枚舉的屬性放在列表中使用for而不是for in進(jìn)行枚舉:

var i;
var properties = [
    'first-name',
    'middle-name',
    'last-name',
    'profession'
];
for (i = 0; i < properties.length; i += 1) {
    document.writeln(properties[i] + ': ' + another_stooge[properties[i]]);
}

刪除

delete操作符用于刪除對(duì)象的屬性,它不會(huì)干涉原型

減少全局變量的使用

減少全局變量的使用可以通過創(chuàng)建一個(gè)單一的全局變量作為你應(yīng)用的容器:

var MyApp = {};
MyApp.var1 = ...;
MyApp.var2 = ...;

還可以使用閉包減少全局變量的使用

函數(shù)


函數(shù)對(duì)象

  • JavaScript中的函數(shù)是對(duì)象
  • 原型為Function.prototype,F(xiàn)unction.prototype的原型為Object.prototype

Function Literals

函數(shù)通過function literal創(chuàng)建

var add = function(a, b) {
    return a+b;
};

調(diào)用

在調(diào)用函數(shù)時(shí),除了聲明的形參之外,還接收了兩個(gè)參數(shù): thisarguments。this的值取決于調(diào)用模式,在JavaScript中有四種調(diào)用模式:

  1. 方法調(diào)用模式(method invocation pattern)

當(dāng)一個(gè)函數(shù)作為一個(gè)屬性存儲(chǔ)在對(duì)象中,稱其為方法,當(dāng)方法被調(diào)用時(shí),this綁定到該對(duì)象上

 var myObject = {
         value: 0,
         increment: function (inc) {
             this.value += typeof inc === 'number' ? inc : 1;
} };
myObject.increment( ); 
document.writeln(myObject.value);             // 1 
myObject.increment(2);
document.writeln(myObject.value);             // 3

方法可以通過this訪問對(duì)象的屬性, this和對(duì)象的綁定時(shí)在方法調(diào)用時(shí)完成的

  1. 函數(shù)調(diào)用模式(function invocation pattern)

若函數(shù)不是對(duì)象的屬性,對(duì)其的調(diào)用為函數(shù)調(diào)用。這種模式的調(diào)用,this被綁定到全局對(duì)象上(global object),這種設(shè)計(jì)使得嵌套函數(shù)中內(nèi)部函數(shù)調(diào)用時(shí)不能直接使用this獲取調(diào)用它的函數(shù)的上下文,當(dāng)然這可通過簡(jiǎn)單的方法解決

myObject.double = function () {
    var that = this;              // Workaround.
    var helper = function () {
        that.value = add(that.value, that.value);
    };
    helper();                     // Invoke helper as a function. 
};
myObject.double( );                    // Invoke double as a method.
document.writeln(myObject.getValue()); // 6
  1. 構(gòu)造函數(shù)調(diào)用模式(constructor invocation pattern)

如果一個(gè)函數(shù)通過new前綴調(diào)用,一個(gè)新的對(duì)象被創(chuàng)建,該對(duì)象隱式關(guān)聯(lián)到該函數(shù)的原型成員,this被綁定到該新建的對(duì)象

var Quo = function(string) {
    this.status = string;
};
Quo.prototype.get_status = function() {
    return this.status;
};
var myQuo = new Quo("Confused");
document.writeln(myQuo.get_status());        // "Confused"

通過前綴new調(diào)用的函數(shù)被稱為構(gòu)造函數(shù)(constructor)

  1. apply調(diào)用模式(apply invocation pattern)

JavaScript中函數(shù)可以有方法,函數(shù)的apply方法可以用來(lái)進(jìn)行函數(shù)調(diào)用,它接收兩個(gè)參數(shù),第一個(gè)時(shí)this要綁定的對(duì)象,第二個(gè)是調(diào)用函數(shù)所需的參數(shù)列表

var add = function(a, b) {return a+b;};
var array = [1,2];
var sum = add.apply(null, array);            // sum = 3
var statusObject = {"status" : "OK"};
Quo.prototype.get_status.apply(statusObject)             // "OK"
(function () {return this.status;}).apply(statusObject)  // "OK"

arguments

函數(shù)的隱式參數(shù)arguments里存放了所有的調(diào)用參數(shù),它不是數(shù)組,只是一個(gè)類似列表的對(duì)象,它有l(wèi)ength屬性,但缺少數(shù)組的其它屬性

返回

函數(shù)可以通過return語(yǔ)句返回一個(gè)值,若是通過構(gòu)造函數(shù)模式調(diào)用(new),沒有通過return返回對(duì)象,則this被返回

異常

var add = function (a, b) {
         if (typeof a !== 'number' || typeof b !== 'number') {
             throw {
                 name: 'TypeError',
                 message: 'add needs numbers'
            }; 
        }
return a + b; 
}
var try_it = function () { try {
             add("seven");
         } catch (e) {
             document.writeln(e.name + ': ' + e.message);
         }
}
try_it( );

throw語(yǔ)句應(yīng)該接收一個(gè)異常對(duì)象,它至少包含兩個(gè)屬性,name表明異常類型和一個(gè)描述異常的message屬性。異常對(duì)象可以被catch語(yǔ)句捕捉

作用域

var foo = function() {
    var a = 3, b = 5;            // 此時(shí)a=3, b=5
    var bar = function() {
        var b = 7, c = 11;       // 此時(shí)a=3, b=7, c=11
        a += b + c;              // 此時(shí)a=21, b=7, c=11
    };
    bar();                       // 此時(shí)a=21, b=5
}

在函數(shù)內(nèi)部定義的變量,函數(shù)外部不可見,對(duì)函數(shù)內(nèi)的所有區(qū)塊都可見。

閉包

對(duì)于JavaScript的作用域,其好的部分是,一個(gè)函數(shù)的內(nèi)部定義的函數(shù)能夠獲取該函數(shù)的除thisarguments的所有參數(shù)和變量的值。
一種極為有趣的情形是內(nèi)部函數(shù)可以有比它外部函數(shù)更長(zhǎng)的生命周期:

var myObject = function () {
    var value = 0;
    return {
        "increment": function(inc) {
            value += typeof inc === "number" ? inc : 1;
        },
        "getValue": function() {
            return value;
        }
    };
}();

Callbacks

// 異步處理請(qǐng)求
request = prepare_the_request();   // 準(zhǔn)備請(qǐng)求
send_request_asynchronously(request, function(response) {
        deal_with(response);
    });                            // 異步發(fā)送請(qǐng)求,將處理請(qǐng)求響應(yīng)的回調(diào)函數(shù)作為參數(shù)傳入

模塊

一個(gè)模塊是提供接口但是隱藏狀態(tài)和實(shí)現(xiàn)的函數(shù)或者對(duì)象??梢酝ㄟ^函數(shù)閉包構(gòu)造模塊。通過函數(shù)構(gòu)造模塊可以幾乎完全消除全局變量,因而可以規(guī)避JavaScript最糟糕的特性。

例如我們要給字符串增加一個(gè)方法deentityify,它用于翻譯HTML實(shí)體到對(duì)應(yīng)的字符。最直觀的實(shí)現(xiàn)方式就是將實(shí)體和對(duì)應(yīng)的字符存放在一個(gè)對(duì)象中,但是在什么地方存放這個(gè)對(duì)象呢?放在全局變量中?呵...,放在函數(shù)中?每次調(diào)用函數(shù)時(shí)解釋器該對(duì)象要重新求值。理想的方式是放在閉包中,甚至可以添加一個(gè)方法用于添加新的HTML實(shí)體

// 給Function.prototype添加一個(gè)新的方法method,該方法接收兩個(gè)參數(shù)
// 字符串name和函數(shù)func。于是所有的函數(shù)都增加了這樣一個(gè)方法(因?yàn)樗?// 函數(shù)的原型鏈的底端都是Function.prototype)
// 當(dāng)一個(gè)函數(shù)調(diào)用這個(gè)method方法時(shí),該函數(shù)為自己的原型添加了一個(gè)
// 名為name的方法(參考上面的方法調(diào)用模式)
Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
};
// 為String的原型添加deentityify方法
String.method("deentityify", function(){
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };
    return function () {
       return this.replace(/&([^&;]+);/g,
           function (a, b) {
               var r = entity[b];
               return typeof r === 'string' ? r : a;
          });
    };
}());
'<">'.deentityify()    // -> <">

該實(shí)現(xiàn)中entity變量?jī)Hdeentityfy方法可以訪問

模塊的一般模式----將一個(gè)函數(shù)作為模塊,在該模塊函數(shù)中可以定義一些私有變量和函數(shù),另有一些特權(quán)函數(shù),這些函數(shù)作為模塊函數(shù)的返回值或者被存放在外部可見的地方,這些特權(quán)函數(shù)可以通過閉包訪問模塊函數(shù)中的私有變量和函數(shù)

這種模式可以消除全局變量,還可以用于生成安全的對(duì)象

var serial_maker = function () {
    var prefix = '',
    var seq    = 0;
    return {
        set_prefix: function (p) {
            prefix = String(p);
        },
        set_seq: function (s) {
            seq = s; },
            gensym: function () {
            var result = prefix + seq; seq += 1;
            return result;
        }
    };
};
var seqer = serial_maker(); 
seqer.set_prefix = ('Q';) 
seqer.set_seq = (1000);
var unique = seqer.gensym();

如果將上面的seqer.gensym提供給第三方使用,該函數(shù)可以生成唯一的標(biāo)識(shí)符但是前綴和序列號(hào)不可更改。

級(jí)聯(lián)(cascade)

如果一個(gè)方法沒有返回值,我們可以令其返回this(即返回調(diào)用該方法的對(duì)象自己),這樣我們就開啟了級(jí)聯(lián)模式。

Curry

JavaScript中可以這樣實(shí)現(xiàn)curry

Function.method("curry", function() {
    // 之前提到過arguments不是數(shù)組,所以這里需要將它轉(zhuǎn)換為數(shù)組(為了使用concat方法)
    var slice = Array.prototype.slice,
          args = slice.apply(arguments), 
          that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
});

Memorization

考慮遞歸求解Fibonacci數(shù)列

var fibonacci = function (n) {
     return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};

這種求解方法非常低效,太多的工作被重做,記住已做的工作:

var fibonacci = function() {
    var memo = [0, 1];
    var fib = function(n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fib(n-1) + fib(n-2);
            memo[n] = result;
        }
        return result
    };
    return fib;
}();

這種方式可以一般化成memorizer,自行實(shí)現(xiàn)吧
func = memorizer(init_values, func)

繼承


創(chuàng)建函數(shù)對(duì)象時(shí),生成函數(shù)對(duì)象的構(gòu)造函數(shù)執(zhí)行類似下面的操作:

this.prototype = {constructor: this};

生成的函數(shù)對(duì)象被賦予了一個(gè)屬性prototype,其(屬性prototype)值為一個(gè)對(duì)象,該對(duì)象有值為生成的函數(shù)對(duì)象的屬性constructor。

函數(shù)的構(gòu)造函數(shù)模式調(diào)用(new前綴調(diào)用)若以函數(shù)的方法實(shí)現(xiàn)的話,可以這樣實(shí)現(xiàn):

Function.method("new", function() {
    # 創(chuàng)建一個(gè)以構(gòu)造函數(shù)原型為原型的對(duì)象 
    var that = Object.create(this.prototype);
    # 調(diào)用構(gòu)造構(gòu)造函數(shù),綁定構(gòu)造函數(shù)的this到新建對(duì)象that
    var other = this.apply(that, arguments);
    # 構(gòu)造函數(shù)返回的是對(duì)象返回該對(duì)象,否則返回新建對(duì)象that
    return (typeof other === 'object' && other) || that;
})
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,130評(píng)論 2 17
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,315評(píng)論 1 10
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 3,083評(píng)論 4 14
  • 昨天在群里又看到我一直奉為經(jīng)典的話:我們還是學(xué)生。 幾年前,我常面試新同事,遇到很多以“我還是個(gè)學(xué)生”為標(biāo)的的道德...
    三摩地閱讀 727評(píng)論 0 0
  • 下午和一位許久沒有聯(lián)系的朋友聊了會(huì),因?yàn)楣ぷ髅β?,大家同在一座城市,也很少有聚?huì)。 今天,看她發(fā)了一個(gè)...
    野蠻超體閱讀 419評(píng)論 2 1

友情鏈接更多精彩內(nèi)容