前端—初級階段4(13-15)—JavaScript語言精粹

內容

ECMAScript核心語法結構:
1.語法
2.對象
3.函數
4.繼承
5.數組
6.正則表達式
7.方法
8.附錄A-毒瘤
9.附錄B-糟粕

1、語法

1.類型、值和變量

1) 類型:區分數據類型javascript

在JS中使用var關鍵詞聲明變量,變量的類型會根據其所賦值來決定(動態類型)。
JS中數據類型分爲原始數據類型(5種)和引用數據類型(Object類型)。
  • 原始數據類型(5):Number、String、Boolean、Undefined、Null。須要注意的是JS中字符串屬於原始數據類型。
  • typeof返回值(6):number、string、boolean、undefined、object、function 。null返回object
  • instanceof:解決引用類型判斷問題 詳解

3) 變量html

  • 變量:局部變量和全局變量
  • 變量提高:
    函數及變量的聲明都將被提高到函數的最頂部。
    變量能夠在使用後聲明,也就是變量能夠先使用再聲明。

2.語句

1) 條件語句java

  • if:
  • switch:

2)循環語句node

  • while
  • for
  • do

3)強制跳轉語句web

  • continue:跳出單次循環
  • break:跳出單層循環
  • return:函數返回語句,可是返回的同時也將函數中止
  • throw:建立或拋出異常

4)應用
1.label應用-跳出多層循環正則表達式

2、對象

1.對象字面量

var obj={
    "last-name":"yue",//'-'是不合法的,必須使用引號
    firstName:"su"
}

2.檢索

1)檢索對象裏包含的值:數組

obj["last-name"] //yue
obj.firstName //su

2)用||填充默認值瀏覽器

var name=obj.name||'susu';

3)用&&避免錯誤安全

obj.likeInfo.model// throw "TypeError"
obj.likeInfo && obj.likeInfo.model //undefined

3.更新

經過賦值語句更新,若是有值則替換,沒有值則新增服務器

obj.firstName='susu'

4.引用

對象經過引用來傳遞。它們永遠不會被複制

var x=obj;
x.nick='aaa';
console.log(obj.nick);//aaa
// x,obj指向同一個對象的引用

var a={},b={},c={}//a,b,c每一個都引用一個不一樣的空對象
var a=b=c={};//a,b,c引用同一個空對象

5.原型

每一個對象都鏈接到一個原型對象,並從中繼承屬性
經過字面量建立的對象都鏈接到Object.prototype
object的create方法能夠選擇某個對象做爲它的原型

if(typeof Object.beget!=='function'){
    Object.create=function(proto){
        function F(){};
        F.prototype=proto;
        return new F();
    }
}
var a=Object.create(obj);

// 當對a對象作出改變時,不會影響該對象的原型

6.反射

檢查對象並肯定對象的屬性
刪除不須要的屬性:
1) 檢查並丟棄值是函數的屬性

typeof obj.toString // 'function'

2) hasOwnProperty 檢查對象本身的屬性,不會檢查原型鏈

obj.hasOwnProperty('toString') //false

7.枚舉

for in:遍歷對象中的全部屬性名,包含原型中本身添加的屬性
注意:屬性名出現的順序是不肯定的,(能夠建立包含屬性名的數組,再用for循環來解決)

8.刪除

delete :刪除對象的屬性,不會觸及原型鏈中的任何對象
若是存在該屬性,則被移除,並返回true;
若是不存在該屬性,返回undefined;

// 不會影響原型
Object.prototype.name='1111111';
delete name;//true
obj.name;//1111111

9.減小全局變量污染

最小化使用全局變量的方法之一:只建立一個惟一的全局變量

var local={
    data:{},
    methods:{
        test(){
            console.log(123)
        }
    }
}
local.methods.test();//123

3、函數

1.函數對象

函數對象鏈接到Function.prototype(該原型對象自己鏈接到Object.prototype)
每一個函數在建立時會附加兩個隱藏屬性:函數的上下文和實現函數行爲的代碼。
每一個函數對象在建立時也隨配有一個prototype屬性。它的值是一個擁有controctor屬性且值即爲該函數的對象。
這和隱藏鏈接到Function.prototype徹底不一樣。

2.函數字面量

函數對象經過函數字面量來建立:

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

函數字面量包含四個部分:
1) 保留字function
2) 函數名:能夠省略(匿名函數),能夠用來遞歸調用本身,
3)參數:
4) 函數主體

3.調用

調用時每一個函數接收兩個附加參數:this,arguments。
調用模式:這些模式在如何初始化this上存在差別
1) 方法調用模式
當一個函數被保存爲對象的一個屬性時,咱們稱它爲一個方法。
當方法調用時,this被綁定到該對象

var myObj={
    value:0,
    increment:function(inc){
        this.value+=typeof inc==='number'?inc:1;
    }

}

myObj.increment(2);
console.log(myObj.value);//3

2) 函數調用模式
當一個函數並不是一個對象的屬性時,那麼它就是被看成函數來調用的。
調用函數時,this被綁定到全局對象。

var sum=add(3,4); //7

解決this指向:在外部函數中定義that=this,那麼內部函數能夠經過that訪問外部的對象。

3) 構造器調用模式
若是在一個函數前面帶上new來調用,那麼背地裏將會建立一個鏈接到該函數的prototype成員的新對象,
同時this會被綁定到新對象上。會改變return語句的行爲。

var Obj=function(str){
    this.status=str;
}
Obj.prototype.get_status=function(){
    return this.status;
}
var myobj=new Obj('aaa');
myobj.get_status();//aaa

4) apply調用模式
aplly(綁定給this的值,參數數組):構建一個參數數組傳遞給調用函數

var statusObj={
    status:123
}
Obj.prototype.get_status.apply(statusObj);//123

4.參數

當函數調用時,會接受一個附加參數arguments數組(類數組:有length屬性,但沒有數組的任何方法)。
能夠編寫一個無須指定參數個數的函數。

var sum=function(){
    var sum=0;
    for(var i=0;i<arguments.length;i++){
        sum+=arguments[i];
    }
    return sum;
}
sum(1,2,3)//6

5.返回

return語句可用來使函數提早返回。
若是使用構造器調用(有new前綴),且返回不是一個對象,則返回this(該新對象)。

6.異常

異常是干擾程序的正常流程的不尋常
throw語句中斷函數的執行,並拋出exception對象,該對象會被傳遞到try語句的catch從句。

var add=function(a,b){
    if(typeof a!=='number'||typeof b !=='number'){
        throw{
            name:'TypeError',
            message:'必須是數字'
        }
    }
    return a+b;
}
try{
    add(1,'aaa')
}catch(e){
    console.log(e.name,e.message);//TypeError 必須是數字
}

7.擴充類型的功能

經過給基本類型增長方法,新的方法馬上被賦予到全部對象的實例上,提升語言的表現力。

Function.prototype.method=function(name,func){
    if(!this.prototype[name]){
        this.prototype[name]=func;
    }
    return this;
}

Number.method('interge',function(){
    return Math[this<0?'ceil':'floor'](this);
});
2.3.interge();//2

8.遞歸

遞歸函數就是會直接或間接的調用自身的一種函數。
遞歸函數操做樹形結構,如瀏覽器端的文檔對象模型(DOM)

// 漢諾塔遊戲
var hanoi=function(n,from,ass,to){
  if(n>0){
    hanoi(n-1,from,to,ass);
    console.log("移動第"+n+"個從"+from+"到"+to);
    hanoi(n-1,ass,from,to);
  }
}

hanoi(2,"A","B","C");
// 移動次數
function moveTimes(n){
    if(n==1){
        return 1;
    }
    return 2*moveTimes(n-1)+1;
}

9.做用域

做用域控制着變量與參數的可見性及生命週期,它減小了名稱衝突,並提供了自動內存管理。
優勢:內部函數能夠訪問定義它們的外部函數的參數和變量(除了this和arguments)。

10.閉包

當內部函數被保存到外部時,將會生成閉包。閉包會致使原有做用域鏈不釋放,形成內存泄漏。
閉包能夠訪問它被建立時所處的上下文環境。

// 避免在循環中建立函數,能夠在循環以外建立一個輔助函數,
// 讓這個輔助函數再返回一個綁定了當前i值的函數

var add_handlers=function(nodes){
    var helper=function(i){
        return function(){
            console.log(i);
        }
    }
    for(var i=0;i<nodes.length;i++){
        nodes[i].onclick=helper(i)
    }
}

add_handlers(document.getElementsByTagName('li'))

11.回調

發起異步請求時,提供一個當服務器的響應到達時隨即觸發的回調函數,異步函數當即返回,這樣客戶端就不會被阻塞。

12.模塊

使用函數和閉包來構造模塊。
模塊模式利用了函數做用域和閉包來建立被綁定對象與私有成員的關聯。
通常形式:一個定義了私有變量和函數的函數;利用閉包建立能夠訪問私有變量和函數的特權函數;最後返回這個特權函數,或者保存到一個可訪問到的地方。

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;
        })
    }
}())

'&gt;&quot;&lt;'.deentityify(); // >"<

13.級聯

若是方法返回this,就會啓用級聯。
在一個級聯中,能夠在單獨一條語句中依次調用同一個對象的不少方法。

14.柯里化

柯里化容許咱們把函數與傳遞給它的參數相結合,產生一個新的函數。

Function.method('curry',function(){
    var slice=Array.prototype.slice,
        args=slice.apply(arguments);
        that=this;
    return function(){
        return that.apply(null,args.concat(slice.apply(arguments)));
    }
});

function add(a,b){
    return a+b;
}
var add1=add.curry(1);
var res=add1(6);//7

15.記憶

記憶:函數能夠將先前操做的結果記錄在某個對象裏,從而避免無謂的重複運算。

// memo:初始數組; formula:函數 公式
var memoizer=function(memo,formula){
    var recur=function(n){
         result=memo[n];
        if(typeof result !=='number'){
            result=formula(recur,n);
            memo[n]=result;
        }
        return result;
    }
    return recur;

}

var fibonacci=memoizer([0,1],function(recur,n){
    return recur(n-1)+recur(n-2);
});
var res=fibonacci(10);

4、繼承

1.僞類

缺點:沒有私有環境,全部的屬性都是公開的。

// 當採用構造器調用模式,函數執行的方式會被修改。
// 若是new運算符是一個方法,它可能會像這樣執行

Function.method('new',function(){
    // 建立一個新對象,它繼承自構造器的原型對象
    var that=Object.create(this.prototype);
    // 調用構造器函數,綁定this到新對象上
    var other=this.apply(that,arguments);
    // 若是它的返回值不是一個對象,就返回改新對象
    return (typeof other=='object'&& other)||that;
})

能夠構造一個僞類來繼承父類,這是經過定義它的constructor函數並替換它的prototype爲一個父類的實例來實現

Function.method('inherits',function(Parent){
    this.prototype=new Parent();
    return this;
})

2.對象說明符

在編寫構造器時讓它接受一個簡單的對象說明符。
多個參數能夠按任何順序排列,若是構造器使用默認值,一些參數能夠忽略,代碼易閱讀。

var obj=testFn({
        first:f,
        middle:m,
        last:l
    });

3.原型

一個新對象能夠繼承一箇舊對象的屬性
用Object.create(parent)構造出更多的實例
差別化繼承:經過定製一個新對象,咱們指明它與所基於的基本對象的區別

// 做用域繼承,內部做用域繼承外部做用域遇到一個左花括號時block函數被調用,
var block=function(){
    // 記住當前做用域,構造一個包含了當前做用域中全部對象的新做用域
    var oldScope=scope;
    scope=Object.create(oldScope);
    // 傳遞左花括號做爲參數調用advance
    advance('{');
    // 使用新的做用域進行解析
    parse(scope);
    // 傳遞左花括號做爲參數調用advance並拋棄新做用域,恢復原來老的做用域。
    advance('}');
    scope=oldScope;
}

4.函數化

應用模塊模式解決私有變量和私有函數

函數化構造器步驟:
1)建立一個新對象
2)有選擇的定義私有實例變量和方法。
3)給這個對象擴充方法,這些方法擁有特權去訪問參數。
4)返回那個新對象

// 僞代碼模板

var constructor=function(spec,my){
    var that,其餘的私有實例變量
        my=my||{};
    把共享的變量和函數添加到my中
    that=一個新對象
    添加給that的特權方法
    return that;
}
// 處理父類的方法
Object.method('superior',function(name){
    var that=this,
        method=that[name];
    return function(){
        return method.apply(that,arguments);
    }
})
//例子
var mammal=function(spec){
    var that={};
    that.get_name=function(){
        return 'hello'+spec.name;
    }
    return that;
}

var cat=function(spec){
    var that=mammal(spec);
    var super_get_name=that.superior('get_name')
    that.get_name=function(){
        return spec.name+'like'+super_get_name();
    }
    return that;
}
var my=cat({name:'su'});
console.log(my.get_name());//su like hello su

5.部件

從一套部件中把對象組裝出來。

5、數組

1.定義:

  • 字面量: var arr=[1,2,3];
  • 構造方法: new Array(length/content);

2.屬性

  • constructor: 返回建立此對象的數組函數的引用。
  • length:設置或返回數組中元素的長度。
  • prototype:向對象添加屬性和方法

3.刪除

1)delete 會在數組中留下一個空洞
2)splice 能夠刪除元素並替換爲其餘元素

var arr=['a','b','c','d'];
delete arr[1];
console.log(arr);//["a", empty, "c", "d"]

// 對於大型數據,可能效率會不高
var arr1=['a','b','c','d'];
arr1.splice(2,1)
console.log(arr1);//["a", "b", "d"]

4.方法

Array.method('reduce',function(f,value){
    for(var i=0;i<this.length;i++){
        value=f(this[i],value)
    }
    return value;
})

function add(a,b){
    return a+b;
}
var arr=[1,2,3];
// 由於數組是對象,能夠給單獨的數組增長一個方法
// 由於字符串total不是整數,因此不會改變數組的長度
arr.total=function(){
    return this.reduce(add,0)
}
console.log(arr.total());//6

Object.create方法用在數組是沒有意義的,由於它產生一個對象,而不是一個數組。
產生的對象將繼承這個數組的值和方法,可是它沒有那個特殊的length屬性。

5.指定初始值

// fill 初始化
var arr=new Array(5);
arr.fill(0);//[0, 0, 0, 0, 0]

// 初始化一維數組
Array.dim=function(dimension,initial){
    var a=[];
    for(var i=0;i<dimension;i++){
        a[i]=initial;
    }
    return a;
}
Array.dim(5,0);//[0, 0, 0, 0, 0]


// 初始化多維數組
Array.matrix=function(m,n,initial){
    var a=[],mat=[];
    for(var i=0;i<m;i++){
        for(var j=0;j<n;j++){
            a[j]=initial
        }
        mat[i]=a;
    }
    return mat;
}
Array.matrix(2,2,0);//[[0,0],[0,0]]

6.原型-方法

1).改變原數組:

  • push:向數組的末尾添加一個或更多元素,並返回新的長度。
  • pop:刪除並返回數組的最後一個元素
  • shift: 刪除並返回數組的第一個元素
  • unshift:向數組開頭添加一個或多個元素,並返回新的長度。
  • reverse:逆轉順序,並返回新數組
  • sort:排序,返回排序後的數組
  • splice:(從第幾位開始,截取多少的長度,在切口處添加新的數據) 刪除元素,並向數組添加新元素,返回被截取元素的數組
  • fill(value, start, end) 將一個固定值替換數組的元素。

2).不改變原數組

  • concat: 鏈接兩個或更多的數組,並返回結果。
  • slice:(從該位開始截取,截取到該位] 截取,返回被截取元素的數組
  • join:經過指定的分隔符(默認逗號)進行分隔,返回字符串
  • toString:把數組轉換爲字符串,並返回結果。

7.原型-循環方法

1)參數相同 (回調函數(當前元素,當前元素的索引,原數組),this)

  • forEach: 代替普通for循環,沒有返回值
  • map:經過指定函數處理數組的每一個元素,並返回處理後的數組。
  • filter:過濾 ,返回符合條件全部元素的數組。
  • every:檢測數值中的每一個元素是否都符合條件。返回true或false
  • some:檢測數組中是否有元素符合條件。返回true或false

5.應用

1.數組-參考手冊
2.數組,類數組
3.forEach,for in ,for of的區別

6、正則表達式

1.結構

1)建立RegExp對象:

  • 字面量 var my_reg=/[A-Z]/g;
  • 構造器,適用於必須在運行時動態生成正則表達式的情形。var my_reg=new RegExp('[A-Z]','g');
    建立字符串時須要注意,由於反斜槓在正則表達式和字符串中含義不一樣,一般須要雙寫反斜槓,以及對引號進行轉義

2)正則表達式標識

標識 含義
g 全局的(匹配屢次;不一樣的方法對g標識的處理各不相同)
i 大小寫不敏感(忽略字符大小寫)
m 多行(^和$能匹配行結束符)

3)RegExp對象的屬性

屬性 用法
global 若是標識g被使用,值爲true
ignoreCase 若是標識i被使用,值爲true
multiline 若是標識m被使用,值爲true
lastIndex 下一次exec匹配開始的索引。初始值爲0
source 正則表達式源碼文本

2.元素

1)正則表達式分支
一個正則表達式包含一個或多個正則表達式序列。
這些序列被|(豎線)字符分隔,若是這些字符中的任何一項符合匹配條件,那麼這個選擇就被匹配。
它常是按順序依次匹配這些序列項

// 由於in已被成功匹配,因此不會匹配int
var a='into'.match(/in|int/);//['in']

2)正則表達式序列
一個正則表達式序列包含一個或多個正則表達式因子。
每一個因子能選擇是否跟隨一個量詞,這個量詞決定着這個因子被容許出現的次數。
若是沒有指定這個量詞,那麼該因子只會被匹配一次

3)正則表達式因子
一個正則表達式因子能夠是一個字符、一個由圓括號包圍的組、一個字符類,或是一個轉義序列
這些字符須要轉義: / { } ? + * | . ^ $

4)正則表達式轉義

反斜槓字符在正則表達式因子和在字符串中均表示轉義,但在正則表達式因子中有點不一樣
\d : [0-9],\D:[^d]
\s : [tnrvf ] 空白字符, \S:[^s]
\w : [0-9A-Za-z_] ,\W:[^w]
\b : 單詞邊界 \B:[^b]
\1 : 指向分組1所捕獲到的文本的一個引用。 如:\2,\3

5)正則表達式分組

  • 捕獲型: 一個捕獲型分組是一個被包圍在圓括號找那個的正則表達式分支。每一個捕獲型分組都被指定了一個數組。
    任何匹配這個分組的字符都會被匹配。
    在正則表達式中第一個捕獲(的是分組1,第二個捕獲(的是分組2。
  • 非捕獲型: 非捕獲型分組有一個(?:前綴,僅作簡單的匹配,並不會捕獲所匹配的文本,不會干擾捕獲型分組的編號。
  • 向前正向匹配: 向前正向匹配分組有一個(?=前綴,相似非捕獲型分組,
    但在這個組匹配後,文本會倒回到它開始的地方,實際上並不匹配任何東西。
  • 向前負向匹配: 向前負向匹配分組有一個(?!前綴,相似向前正向匹配分組,
    但只有當它匹配失敗時它才繼續向前進行匹配

6)正則表達式字符集
正則表達式字符集是一種指定一組字符的便利方式。
若是匹配一個元音字母,能夠寫成(?:a|e|i|o|u),
但能夠更方便的寫成一個類[aeiou], 類的求反 [^aeiou]

7)正則表達式量詞

正則表達式因子能夠用一個正則表達式量詞後綴來決定這個因子應該被匹配的次數。
包圍在一對花括號中的一個數組表示這個因子被匹配的次數。
n+ : {1,} 1到多個
n* : {0,}
n? : {0,1}
n{X} : X個
n{X,Y} : X-Y個
n{X,} : X到多個
?=n : 匹配其後緊接n 的字符串
?!n : 匹配其後沒有緊接n 的字符串
若是隻有一個量詞,表示趨向於進行貪婪性匹配,即匹配儘量多的副本直至達到上限。
若是這個量詞附加一個後綴?,表示趨向於進行非貪婪性匹配,即只匹配必要的副本就好。

7、方法

1.Array

數組-參考手冊

2.Function

function.apply(thisArg,argArray)
apply方法調用function,傳遞一個會被綁定到this上的對象和一個可選的數組做爲參數

Function.method('bind',function(that){
    var method=this;
    var slice=Array.prototype.slice;
    var args=slice.apply(arguments,[1])
    return function(){
        return method.apply(that,args.concat(slice.apply(arguments)));
    }
})
var test=function(){
    return this.value+':'+Array.prototype.slice.apply(arguments);
}.bind({value:666},1,2);
console.log(test(3));//666:1,2,3

3.Number

Number-參考手冊

4.Object

Object.hasOwnProperty(name)不會檢查原型鏈中的屬性

var a={member:true};
var b=Object.create(a);
var c=a.hasOwnProperty('member');//true
var d=b.hasOwnProperty('member');//false
var e=b.member;//true

5.RegExp

RegExp-參考手冊

1.regExp.exec(string)
若是經過循環去查詢一個匹配模式在字符串中發生了幾回,須要注意:若是提早退出了循環,再次進入這個循環前必須把
RegExp.lastIndex重置爲0,並且,^僅匹配RegExp.lastIndex爲0的狀況。
若是帶有g全局標識,查找不是從這個字符串的起始位置開始,而是從regExp.lastIndex位置開始。

var reg1=/ab/g;
var str='ababab';

console.log(reg1.exec(str),reg1.lastIndex)
console.log(reg1.exec(str),reg1.lastIndex)
console.log(reg1.exec(str),reg1.lastIndex)
console.log(reg1.exec(str),reg1.lastIndex)
console.log(reg1.exec(str),reg1.lastIndex)
// 打印結果:
["ab", index: 0, input: "ababab", groups: undefined] 2
["ab", index: 2, input: "ababab", groups: undefined] 4
["ab", index: 4, input: "ababab", groups: undefined] 6
null 0
["ab", index: 0, input: "ababab", groups: undefined] 2

2.regExp.test(string)

// 若是這個方法使用g標識,會改變lastIndex的值
var reg=/[A-Z]/g;
var arr=['Asdsd','BsdsCds','Dasas','E1212'];
for(var i=0;i<arr.length;i++){
    console.log(reg.test(arr[i]),reg.lastIndex);//true 1,true 5,false 0,true 1
}

6.String

字符串-參考手冊

附錄A-毒瘤

1.全局變量

全局變量使得在同一個程序中運行獨立的子程序變得更難。
由於全局變量能夠被程序的任何部分在任意時間修改,使程序的行爲變得極度複雜,下降程序的可靠性

// 定義全局變量的方法
var foo=value;
window.foo=value;
foo=value;

2.做用域

沒有塊級做用域,代碼塊中聲明的變量在包含此代碼塊的函數的任何位置都是可見的。
更好的方式:在每一個函數的開頭部分聲明全部變量

3.自動插入分號

有一個自動修復機制,它試圖經過自動插入分號來修正有缺損的程序.
它可能會掩蓋更爲嚴重的錯誤。

// 在return語句後自動插入分號致使的後果
function test1(){
    return{
        name:'aaa'
    }
}
function test2(){
    return
    {
        name:'aaa'
    }
}
console.log(test1(),test2());//{name: "aaa"} undefined

4.保留字

保留字不能被用來命名變量或參數。
當保留字被用作對象字面量的鍵值時,它們必須被引號括起來,不能用在點表示法中,必須使用括號表示法。

var case;//非法,都不支持
// ie8及如下不支持 非法的寫法
var obj={case:'123'};//非法
var a=obj.case//非法
var object={'case':'1212'};//ok
var b=object['case'];//ok

5.Unicode

Unicode把一對字符視爲一個單一的字符,而js認爲一對字符是兩個不一樣的字符

6.typeof

typeof null返回object
正則表達式,返回object,在safari(3.x版本)中返回function

// 區分null與對象
if(value&& typeof value=='object'){
    //value是一個對象或數組
}

7.parseInt

parseInt把字符串轉換爲整數,

// 遇到非數字時會中止解析
parseInt('16')==parseInt('16aa123');//ture

// ie8及如下會出現如下問題:
// 若是字符串第一個字符是0,那麼字符串會基於八進制來求值,()
// 會致使程序解析日期和時間時出現問題,能夠用parseInt的第二個參數做爲基數解決

console.log(parseInt('08'),parseInt('09'));//0 0
console.log(parseInt('08',10),parseInt('09',10));//8 9

8.+

+運算符能夠用於加法運算或字符串鏈接,如何執行取決於其參數的類型
加法運算:兩個運算數都是數字
字符串鏈接:其中一個運算數是空字符串,則另外一個運算數被轉換成字符串進行鏈接。

9.浮點數

二進制的浮點數不能正確處理十進制的小數。

0.1+0.2 //0.30000000000000004
//浮點數中的整數運算是正確的,因此小數表現出來的錯誤能夠經過指定精度來避免
(0.1*10+0.2*10)/10 //0.3

10.NaN

NaN是一個特殊的數量值。它表示的不是一個數字,可是 typeof NaN==='number'
該值會在試圖把非數字形式的字符串轉化爲數字時產生,如:Number('12px') //NaN
NaN===NaN //fales

// 區分數字與NaN
isNaN(NaN);//true
isNaN('aaa');//true

判斷一個值是否可用作數字使用isFinite函數,由於它會篩掉NaN和Infinity
可是它會試圖把運算數轉換爲一個數字,isFinite('10')//true

var isNumber=function(value){
    return typeof value==='number'&&isFinite(value)
}
isNumber('10');//false

11.僞數組

// 辨別數組
// arguments是一個類數組,返回[object Arguments]
if(Object.prototype.toString.apply(value)==='[object Array]'){
    //value是一個數組
}

12.假值

如下這些值所有等同於假,但它們是不可互換的

類型
0 Number
NaN(非數字) Number
''(空字符串) String
false Boolean
null Object
undefined Undefined

13.hasOwnProperty

hasOwnProperty能夠解決for in的隱患
但它是一個方法,在任何對象中,它可能會被一個不一樣的函數甚至一個非函數的值所替換

var name,obj={};
obj.hasOwnProperty=null;//地雷
for(name in obj){
    if(obj.hasOwnProperty(name)){//觸雷
        console.log(name,obj[name])
    }
}

14.對象

js對象永遠不會是真的空對象,由於它能夠從原型中取得成員屬性

// 若是字符串裏面包含constructor,會返回{hello: 3, word: 1, constructor: "function Object() { [native code] }1"}
// 能夠用hasOwnProperty處理 返回{hello: 1, word: 1, constructor: 1}
var text='hello word hello,Hello constructor';
var words=text.toLowerCase().split(/[\s,.]+/);
var count={},word;
for(var i=0;i<words.length;i++){
    word=words[i];
    if(Object.hasOwnProperty(count[word])&&count[word]){
        count[word]+=1;
    }else{
        count[word]=1;
    }
}
console.log(count);

附錄B-糟粕

1.==

==,!=運算符只有在兩個運算類型一致時纔會作出正確的判斷,若是兩個運算數是不一樣的類型,它們試圖去強制轉換值的類型
==,!=運算符缺少傳遞性,因此使用===和!==

'' == '0'//false
0 == '0' //true
false == 'false'//false
false == '0' //true

false == undefined //false
false == null //fasle
null == undefined //true
'\t\r\n' == 0 //true

2.with語句

with語句本意是用來快捷的訪問對象的屬性,但有時不可預料,因此應該避免使用它
嚴重影響了js處理器的速度,由於它阻斷了變量名的詞法做用域綁定

with(obj){
    a=b;
}
// 和下面的代碼作的是一樣的事情
if(obj.a===undefined){
    a = obj.b === undefined ? b : obj.b;
}else{
    obj.a = obj.b === undefined ? b : obj.b;
}
// 因此,它等同於這些語句中的某一條:
a = b;
a = obj.b;
obj.a = b;
obj.a = obj.b;

3.eval

eval函數傳遞一個字符串給javaScript編輯器,而且執行結果
1)使用eval形式的代碼更加難以閱讀,這種形式使得性能顯著下降,由於它須要運行編譯器,但也許只是爲了執行一個賦值語句。
2)它會讓JSLint失效,讓此工具檢測問題的能力大打折扣
3)減弱了應用程序的安全性,由於它被求值的文本授予了太多的權利,與with語句執行方式同樣,下降語言的性能
4)Function構造器是eval的另外一種形式,應該避免使用(new Function ([arg1[, arg2[, ...argN]],] functionBody))
5)setTimeout,setInterval當接收字符串參數時,也會像eval那樣處理,應避免使用字符串參數

4.continue語句

continue語句跳到循環的頂部
可是若是一段代碼經過重構移除continue語句以後,性能都會獲得改善

console.time(111)
var sum=0;
for(var i=0;i<100;i++){
    if(i==50){
        continue;
    }
    sum+=i;
}
console.timeEnd(111);//0.051025390625ms


console.time(222)
var sum=0;
for(var i=0;i<100;i++){
    if(i!=50){
        sum+=i;
    }
}
console.timeEnd(222);// 0.02099609375ms

5.switch穿越

除非你明確地中斷流程,不然每次條件判斷後都穿越到下一個case條件。

6.缺乏塊的語句

if,while,do,for語句能夠接受一個括在花括號找那個的代碼塊,也能夠接受單行語句
可是單行語句模糊了程序的結構,使得在隨後的操做代碼中可能很容易插入錯誤

//容易致使錯誤
if(ok)
    t=true;
    advance()

7.++

這兩個運算符鼓勵了一種不夠謹慎的變成風格,大多數的緩衝區溢出的錯誤所形成的安全漏洞,都是由像這樣的代碼致使的
而且它會使代碼變得過於擁擠,複雜和隱晦

8.位運算符

在java裏,位運算符處理的是整數,可是js沒有整數類型,只有雙精度的浮點數,所以,位操做符把它們的數字運算數先轉換成整數,在執行運算,而後在轉換回去。
在大多數語言中,位運算符接近於硬件處理,因此很是快。但js的執行環境通常接觸不到硬件,因此很是慢,js不多被用來執行位操做

9.function語句對比function表達式

function語句在解析時會發生被提高的狀況,無論function被放置在哪裏,它會被移動到被定義時所在做用域的頂層,
這放寬了函數先聲明後使用的要求,這會致使混亂

function foo(){

}
// foo是一個包含一個函數值的變量,函數就是數值
var foo=function(){

}

一個語句不能以一個函數表達式開頭,但能夠把函數調用括在一個圓括號之中

(function(){
    var a=1;
    // 這個函數可能對環境有一些影響,但不會引入新的全局變量
})()

10.類型的包裝對象

new Boolean,new Number,new String會返回一個對象,該對象有一個valueOf方法會返回被包裝的值
避免使用new Array和new Object,可以使用[]和{}來代替

11.new

1)new運算符建立一個繼承於其構造器函數的原型的新對象,而後調用該構造器函數,把新建立的對象綁定給this,
這給構造器函數一個機會在返回給請求者前自定義新建立的對象
2)若是忘記new運算符,就是一個普通的函數調用,this被綁定到全局對象,而不是建立一個新對象。污染全局變量
3)構造器函數應該以首字母大寫的形式命名,而且首字母大寫的形式應該只用於來命名構造器函數

12.void

在不少語言中,void是一種類型,表示沒有值
但在js中,void是一個運算符,它接受一個運算數並返回undefined,這沒有什麼用,應避免使用它

//當用戶連接時,void(0) 計算爲 0,但 Javascript 上沒有任何效果。
<a href="javascript:void(0)">單擊此處什麼也不會發生</a>
相關文章
相關標籤/搜索