《JavaScript語言精粹》學習筆記

一.in的用法

  1. for...injavascript

    枚舉一個對象的全部可枚舉屬性java

  2. 檢測DOM/BOM屬性程序員

    if ("onclick" in elem) {
        // 元素支持onclick
    }
    if ("onerror" in window) {
        // window支持onerror
    }
  3. 檢測js對象的原型屬性(結合hasOwnProperty()函數)正則表達式

    if ("attr" in obj && !obj.hasOwnProperty("attr")) {
        // obj有原型屬性attr
    }

    注意:原型屬性會被同名的實例屬性屏蔽掉,因此沒法檢測這樣的:json

    function Super(){
        this.attr = 1;
    }
    function Sub(){
        this.attr = 2;
    }
    Sub.prototype = new Super();
    var obj = new Sub();
    alert(("attr" in obj && !obj.hasOwnProperty("attr")));  /// false

二.||運算符和&&運算符

  • ||能夠用來填充默認值,例如:數組

    var obj = {};
    obj.name = "";  // 注意js有一堆假值:+0、-0、0、NaN、null、undefined、""、''、false
    var name = obj.name || "ayqy";
    alert(name);

    P.S.我的認爲這個東西並很差用,除非能肯定原屬性值不多是任何形式的假值,不然默認值會覆蓋原值瀏覽器

  • &&能夠用來避免從undefined值讀取屬性引發TypeError,例如:緩存

    var obj = {};
    //var attr = obj.obj.attr;   // 報錯,TypeError,不能從undefined值讀取屬性
    var attr = obj.obj && obj.obj.attr; // 只有obj.obj存在且不是假值時纔會取其attr

    P.S.我的認爲和&&差很少,不如直接用if檢測,不只能展示更清晰的邏輯流,還便於添加更多的檢測條件閉包

P.S.這兩種用法是在書的第一部分介紹的,可能更偏向於展現語言特性,並非推薦用法app

我的建議不要這樣用,固然,看到這樣的代碼要能明白其做用與漏洞

三.減小全局變量污染

  1. 用「命名空間」,即空對象,實質是把建立的全局變量減小到1個,好比YUI的Y對象,JQuery的JQuery和$對象。。例如:

    var app = {};   //命名空間:app
    app.Consts = {
        // 子命名空間:常量
    
        URL : {
            // 子子命名空間:URL
        }
    }
    app.Modules = {
        // 子命名空間:模塊
    }
    app.Data = {
        // 子命名空間:數據
    }
  2. 用IIFE(匿名函數當即執行),實質徹底不建立全局變量了,0污染

    (function(){
        // Module1
    })();
    (function(){
        // Module2
    })();

    缺點很明顯,沒法實現對象緩存和共享,出了閉包就沒了

因此通常作法是結合命名空間和IIFE,總體用命名空間組織起來,在模塊內部合適的地方用IIFE

四.4種調用模式

  • 方法調用模式:obj.fun();或者obj[」fun」]();

  • 函數調用模式:fun();此時this指向global對象

  • 構造器調用模式:new Fun();新對象的prototype指向Fun的prototype,this指向這個新對象

  • apply調用模式:fun.apply(this, arrArgs);

五.關於arguments對象

arguments對象不是數組對象,也不支持全部數組方法,只是一個有點特殊的普通對象,特殊在其length屬性(動態更新)

能夠作以下測試:

function fun(){
    var x = Object.prototype.toString.call(arguments);
    alert(x);
    var arr = [];
    alert(Object.prototype.toString.call(arr));
};

fun();
// IE8:[object Object]和[object Array],Chrome:[object Arguments]和[object Array]
// 不同沒關係,反正都不是Array

一個小技巧:能夠用slice方法把arguments對象轉換爲數組,例如:

function fun(){
    //arguments.sort();   // 報錯,不支持sort()函數
    var arr = Array.prototype.slice.call(arguments);    // 轉換
    arr.sort(); //不報錯,說明轉換成功
    alert(arr);
};

fun(3, 2);  // 2, 3

注意:只有slice有這種奇效,concat就沒有,雖然無參的slice和無參的concat用於數組的效果同樣(都是複製一份)

六.級聯(鏈式調用)的實現方式

讓沒有返回值的函數返回this,因此支持鏈式調用,好比JQuery中的:

$("#adBlock").removeClass("active").hide();

七.構造函數調用方式不當會引發做用域污染

若是不用new操做符,直接調用構造函數,好比Fun();此時this指向global屬性,即瀏覽器環境下的window對象,可能會污染全局做用域

八.把形參列表簡化爲單一對象

例如:

function fun(height, width, margin, padding){   // 參數太長,順序記不住
    // do something
}

/*
 * @param {number} arg.height 高度
 * @param {number} arg.width 寬度 
 * @param {number} arg.margin 留白
 * @param {number} arg.padding 補白
 */
function fun(arg){  // 不用記參數順序了
    // do something
}

好處以下:

  1. 調用時不用再關心參數順序了,接口變得更加易用

  2. 能夠直接傳個JSON對象進去

缺點:要寫一堆註釋說明具體須要哪些參數,由於經過一個arg徹底看不出來

九.防僞對象(持久性的對象)

防僞對象的屬性能夠被替換或者刪除,但該對象的完整性不會受到損害

也被稱爲持久性的對象,一個持久性對象就是一個簡單功能函數的集合

P.S.防僞對象的概念出如今書的[函數化]部分,目前還不能徹底理解函數化想要表達的意思,把本身養肥了再看

十.數組

本質是鍵值對,也就是對象,因此並無訪問速度上的優點

區別是Array.prototype提供了不少數組操做函數,此外還有特殊的length屬性

注意: 1. 數組實際佔用的內存空間

var arr = [1];
arr[99] = 100;

此時arr指向的值並無佔用100個元素的空間,由於上面的結果至關於:

var arr = {
    "0" : 1,
    "99" : 100
};

 

  1. length屬性是可寫的

    能夠用arr.length = n;刪掉下標值 >= n的全部元素

  2. 常見的length屬性用法

    省計數器的方式:arr[arr.length] = value;

    或者更簡單的:arr.push(value);

  3. 數組類型檢測

    由於typeof arr返回」object」,並且instanceof操做符在跨frame時失效,因此檢測數組類型不太容易

    書上給了一種超麻煩的方式:

    function isArray(value){
        return value &&
            typeof value === "object" &&
            typeof value.length === "number" &&
            typeof value.splice === "function" &&
            !(value.propertyIsEnumerable("length"));
    }

    其實有在做者寫書的時候還沒出現的簡單方法:

    能夠用Object.prototype.toString.call(value) === ‘[object Array/Function…]’來作類型檢查,也能夠用來區分原生對象和自定義對象,例如:

    [object JSON]   // 原生JSON對象
    [object Object] // 自定義JSON對象

    關於類型檢測的更多信息請查看黯羽輕揚:JS學習筆記11_高級技巧

十一.正則表達式

js的正則表達式功能並不完整,但基本夠用,好比只支持3種模式:g/i/m ~ 全局模式/忽略大小寫模式/多行模式

並且支持的特殊元字符(\d、\s之類的)也比較少,但好在支持非捕獲型分組和正則環視,仍是很不錯的

因此在js中使用正則表達式須要作更多的測試,更多關於正則表達式的信息請查看黯羽輕揚:正則表達式學習筆記

還有一個不經常使用的點:RegExp對象的屬性

  • global:是否開了g模式

  • ignoreCase:返回是否開了i模式

  • lastIndex:返回下一次exec匹配的起始位置

  • multiline:返回是否開了多行模式

  • source:返回正則表達式串

須要特別注意:用字面量方式建立的RegExp對象可能引用同一個實例,例如:

var regex1 = /abc/g;
var regex2 = /abc/g;
var str = "aaa\r\nabc\r\naaa";

alert([regex1.lastIndex, regex2.lastIndex]);    // 0, 0
regex1.test(str);
alert([regex1.lastIndex, regex2.lastIndex]);    // 8, 0
alert(regex1 === regex2);   // false

老版本瀏覽器最後兩行會輸出8, 8和true,聽說正則字面量共享實例是ES3的規定,本機測試發現IE8不存在問題,但理論上IE6就存在這個問題(出IE6的時候,ES5還沒出)

十二. 原生對象操做函數

P.S.此處只介紹須要特別注意的點,完整的各類操做函數請查看黯羽輕揚:JS學習筆記1_基礎與常識

1.數組操做函數

  1. arr1.push(arr2);會把arr2做爲單個元素插入,例如:

    var arr1 = [1];
    var arr2 = [2, 3];
    arr1.push(arr2);
    alert(arr1[2]); // undefined
    alert(arr1[1][1]);  // 3
  2. arr.shift()比arr.pop()慢不少,由於刪掉首元須要更新全部元素的索引,而刪掉尾元不須要

  3. arr.unshift(),在數組頭部插入元素,IE6返回undefined,原本應該返回新數組的長度

2.對象操做函數

obj.hasOwnProperty(「hasOwnProperty」)返回false

3.正則表達式對象操做函數

regex.exec(str)函數功能最強大,固然,執行速度也最慢

regex.test(str)最簡單,最快,注意:不要開g模式,由於純屬浪費(test只返回匹配/不匹配,一次掃描就夠了)

4.字符串操做函數

  • str.replace(regex, fun);是很好用的一個函數,能夠用regex匹配目標部分,還能夠用fun進一步處理匹配的部分

    特別注意:若是regex沒有開g模式,那麼只替換第一個匹配部分,並非全串替換

  • str.replace(regex, replacement);的replacement部分中的$有特殊含義:

    • $$:表示$,若是替換部分中有$須要用$來轉義

    • $&:表示整個匹配的文本

    • $n:例如$1, $2, $3...表示捕獲到的文本

    • $`:表示位於匹配部分以前的文本

    • $’:表示位於匹配部分以後的文本

    更多詳細示例請查看W3School:JavaScript replace() 方法

  • str.split(regex);存在一個特例,須要特別注意

    var str = "a & b & c";    // &是分隔符
    var arr = str.split(/\s*(&)\s*/);   // 含捕獲
    var arr2 = str.split(/\s*&\s*/);    // 不含捕獲
    var arr3 = str.split(/\s*(?:&)\s*/);    // 含非捕獲
    alert(arr); // [a, &, b, &, c]
    alert(arr2);    // [a, b, c]
    alert(arr3);    // [a, b, c]

    若是不當心用了含捕獲型分組的正則表達式,結果可能與預期的不一樣

十三.糟粕

P.S.這裏不打算原樣照搬書上的內容,只給出筆者贊成的最糟糕的地方

  • 自動插入分號。確實很差,好比典型的ASI錯誤:

    function fun(){
        return
        {
            attr : 1;
        }
    }
    
    alert(fun().attr);  // TypeError: Cannot read property 'attr' of undefined
  • typeof。很差用,這是設計上的失誤,歷史緣由

  • paseInt()與八進制。一個很隱蔽的錯誤

    var year = "2015";
    var month = "08";
    var day = "09";
    
    alert(parseInt(year));  // IE8:2015 Chrome:2015
    alert(parseInt(month)); // IE8:0    Chrome:8
    alert(parseInt(day));   // IE8:0    Chrome:9

    由於0開頭的數值被認爲是八進制,省略第二個參數的parseInt()會把08/09看成八進制來解析,因此結果是0

    處理時間日期太容易引發解析錯誤了,因此最好不要省略parseInt()的第二個參數

    P.S.Chrome中正常了,多是某個ES版本對parseInt()作了修改,Chrome作了實現。因此糟粕並非永遠存在的,固然,是否是糟粕也要看程序員怎麼用。。

  • Unicode。js對Unicode的支持很差,雖然也是歷史緣由,但不支持就是不支持

    P.S.出js的時候Unicode本身也沒想到能有100萬個字符,js的字符是16位的,能對應前65536個Unicode字符,而Unicode爲了擴容,就把剩下的每一個字符都用一對字符來表示,但js會把這樣的字符看成兩個字符,因此。。

十四.雞肋

P.S.同上,只列筆者贊成的

  • continue性能低。能夠if消除

  • 位運算性能低。(其它的書都沒有提到這一點)由於要折騰:double -> int -> 作位運算 -> double

  • new到底應該用仍是不用。用的話可能污染做用域

  • void運算符。單目運算,返回undefined,因此能夠用來實現IIFE,例如:

    void function(){
        // do something
    }();

    做者建議不要用void,由於沒什麼人知道它是作什麼的

十五.JSON

整數的首位不能是0,道格拉斯(發明JSON的大爺)本人說的,數值能夠是整數、實數或者科學計數,但首位不能是0,爲了不八進制歧義

測試代碼以下:

var json = '{"number" : 9}';
var obj = JSON.parse(json);
alert(obj.number);  // 9

var json = '{"number" : 09}';   // 注意前導0
var obj = JSON.parse(json);
alert(obj.number);  // 報錯
// Chrome:SyntaxError: Unexpected number
// IE8:語法錯誤
// FF:SyntaxError: JSON.parse: expected ',' or '}' after property value in object

參考資料:

  • 《JavaScript語言精粹》
相關文章
相關標籤/搜索