六天玩轉javascript:javascript變量與表達式(2)

本系列內容爲本人平時項目實踐和參照MDN,MSDN,《javascript語言精粹》,《Effective Javascript》等資料,而且整理本身EverNote的平常積累整理所寫,時間太長有一些代碼樣例來源不可考。本系列不容許任何形式的轉載,謝謝。 from yeanzhijavascript

大綱

第一天:javascript變量,操做符與變量做用域

次日:javascript函數

第三天:對象與原型

第四天:再談函數與繼承

第五天:內置對象

第六天:特殊性質與技巧

第一天:javascript變量與表達式(2)

運算符

JavaScript中的運算符有如下幾種java

  • 賦值運算符
  • 比較運算符
  • 算術運算符
  • 位運算符
  • 邏輯運算符
  • 字符串運算符
  • 特殊運算符

本文咱們只對幾個重要或者有特殊技巧的運算符進行介紹,着重簡介特殊運算符(操做符)node

邏輯運算符

邏輯運算符一般用於布爾型(邏輯)值;這種狀況,它們返回一個布爾型值。然而,&&和||運算符實際上返回一個指定操做數的值,所以這些運算符也用於非布爾型,它們返回一個非布爾型值。
技巧:正則表達式

在條件中使用邏輯與或express

var foo = 10;  
foo == 10 && doSomething(); //等同於if (foo == 10) doSomething(); 
foo == 5 || doSomething(); //等同於if (foo != 5) doSomething();

邏輯或還可用來設置默認值,好比函數參數的默認值。
arg1 = arg1 || 10;數組

特殊運算符

JavaScript有下列特殊運算符:瀏覽器

  • 條件運算符
  • 逗號運算符
  • delete
  • in
  • instanceof
  • new
  • this
  • typeof
  • void

1,條件運算符(惟一的三目運算符)
condition ? val1 : val2
2,逗號操做符
逗號操做符(,)對兩個操做數進行求值並返回第二個操做數的值。它經常用在for循環中,在每次循環時對多個變量進行更新。好比:閉包

for (var i = 0, j = 9; i <= 9; i++, j--)
    expression

關於循環技巧app

var sum = 0;  
for (var i = 0, len = arrayNumbers.length; i < len; i++) {  
    sum += arrayNumbers[i];  
}
//還有一種寫法
for (var i = 0, item; item = a[i++];) {
    // Do something with item
}
//這裏咱們使用了兩個變量。
//for 循環中間部分的表達式仍然用來判斷是否爲真—若是爲真,那麼循環繼續。
//由於i每次遞增 1,這個數組的元素會被逐個傳遞給 item 變量。
//當遇到一個假值元素(如undefined)時,循環結束。

這裏並不推薦for in,若是有人向 Array.prototype 添加了新的屬性,使用這樣的循環這些屬性也一樣會被遍歷:框架

for (var i in a) {
  // Do something with a[i]
}

3,delete
刪除操做符刪除一個對象的屬性.它不會觸及原型鏈中的任何屬性
注意:
3.1,在嚴格模式中,若是屬性是一個不可配置(non-configurable)屬性,刪除時會拋出異常,非嚴格模式下返回 false。其餘狀況都返回 true。
3.2,delete 操做符與直接釋放內存(只能經過解除引用來間接釋放)沒有關係
3.3,一些對象的屬性不能被delete.ECMA262規範中把這些屬性標記爲DontDelete

x = 42;        // 隱式聲明的全局變量
var y = 43;    // 顯式聲明的全局變量
myobj = new Number();
myobj.h = 4;    // 添加屬性h
myobj.k = 5;    // 添加屬性k

delete x;       // 返回 true (隱式聲明的全局變量能夠被刪除)
delete y;       // 返回 false (顯式聲明的全局變量不能被刪除,該屬性有DontDelete標記)
delete Math.PI; // 返回 false (內置對象的內置屬性不能被刪除, 該屬性有DontDelete標記)
delete myobj.h; // 返回 true (用戶定義的屬性能夠被刪除)
with(myobj) { 
  delete k;    // 返回 true (至關於delete myobj.k)
} 
delete myobj;   // 返回 true (隱式聲明的全局變量能夠被刪除)

3.4,不能刪除一個對象從原型繼承而來的屬性(不過能夠從原型上直接刪掉它).

function Foo(){}
 Foo.prototype.bar = 42;
 var foo = new Foo();
 delete foo.bar;           // 無效的操做
 alert(foo.bar);           // alerts 42, 繼承的屬性
 delete Foo.prototype.bar; // 直接刪除原型上的屬性
 alert(foo.bar);           // alerts "undefined",已經沒有繼承的屬性

3.5,刪除數組元素
當刪除一個數組元素時,數組的 length 屬性並不會變小。例如,若是刪除了a[3], a[4]仍然是a[4], a[3]成爲undefined.
當用 delete 操做符刪除一個數組元素時,被刪除的元素已經徹底不屬於該數組。下面的例子中, name[3] 被使用delete完全刪除。

var name = ["ye","an","z","h","i"];
delete name[3];
console.log(name);//[ 'ye', 'an', 'z', , 'i' ]
console.log(name[3]);//undefined
console.log(name.length);//5
for(var v in name){
    console.log(v);//0 1 2 4 
}

若是你想讓一個數組元素的值變爲 undefined 而不是刪除它,可使用 undefined 給其賦值而不是使用 delete 操做符。
數組元素刪除應使用splice函數。

4,函數表達式
function 關鍵字可用來在一個表達式中定義一個函數。上一篇在閉包的地方有函數表達式的例子
函數表達式(function expression)很是相似於函數聲明(function statement),而且擁有幾乎相同的語法。函數表達式與函數聲明的最主要區別是函數名稱,在函數表達式中可忽略它,從而建立匿名函數。

匿名函數建立遞歸技巧
匿名函數能夠經過arguments.callee 的屬性來完成遞歸調用。這個屬性一般指向當前的(調用)函數,所以它能夠用來進行遞歸調用:

var charsInBody = (function(elm) {
    if (elm.nodeType == 3) { // TEXT_NODE
        return elm.nodeValue.length;
    }
    var count = 0;
    for (var i = 0, child; child = elm.childNodes[i]; i++) {
        count += arguments.callee(child);
    }
    return count;
})(document.body);

5,get,set操做符
get,set語法將對象屬性綁定到該屬性查找時將調用的函數。

var log = ['test'];
var obj = {
    get latest () {
        if (log.length == 0) return undefined;
        return log[log.length - 1]
    },
    set current (str) {
        log[log.length] = str;
    }
}
obj.current = 'fa';
console.log (obj.latest);//輸出 "test".

7,in
若是指定的屬性存在於指定的對象中,則 in 運算符會返回 true。

// 數組
var trees = new Array("ye", "an", "zhi");
0 in trees        // true
6 in trees        // false
"bay" in trees    // false (必須使用索引號,而不是數組元素的值)
"length" in trees // true (length是一個數組屬性)

使用delete運算符和將屬性賦值爲undefined

var name = ["ye","an","z","h","i"];
delete name[3];
3 in name; // 返回false

8,instanceof
instanceof 運算符能夠用來判斷某個構造函數的prototype屬性是否存在另一個要檢測對象的原型鏈上。
這個運算符將在《對象與原型》中講解

9,let 操做符
在上一篇變量做用域中有講解

10,new 操做符
這個運算符將在《對象與原型》中講解

11,this操做符
JavaScript函數中的this關鍵字的行爲相比起其餘語言有不少不一樣。在JavaScript的嚴格和非嚴格模式下也略有區別。在絕大多數狀況下,函數的調用方式決定了this的值。this不能在執行期間被賦值,在每次函數被調用時this的值也可能會不一樣。ES5引入了bind方法來設置函數的this值,而不用考慮函數如何被調用的。
11.1 Global context 全局上下文
在全局運行上下文中(在任何函數體外部),this 指代全局對象,不管是否在嚴格模式下。
this===window //true

11.2 Function context 函數上下文
在函數內部,this的值取決於函數是如何調用的。
非嚴格模式下,this的值是一個對象且默認是全局對象

function f1(){
  return this;
}

f1() === window; // global object

嚴格模式下 上面應該返回 undefined ,可是這個並無在瀏覽器中的到普遍的支持,通常會返回一個window 對象

11.3As an object method 做爲對象方法
函數以對象裏的方法的方式進行調用時,它們的this由調用該函數的對象進行設置。

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // logs 37

11.4 As a constructor 做爲構造函數
所謂構造函數,就是經過這個函數生成一個新對象(object)。這時,this就指這個新對象。

 var x = 2;
 function test(){
   this.x = 1;
 }
 var o = new test();
 alert(x); //2
 alert(o.x); //1

11.5 apply調用
apply()是函數對象的一個方法,它的做用是改變函數的調用對象,它的第一個參數就表示改變後的調用這個函數的對象。所以,this指的就是這第一個參數。也就是說js裏面的this是能夠改變的

var val = hi;
    function Test(hello){
        this.val = hello;
    }
    Test.prototype.printval = function(){
        console.log(this.val);
    }
    var o = new Test('hello');
    o.printval();//hello
    o.printval.apply();//0
    o.printval.apply(o);//hello

apply()的參數爲空時,默認調用全局對象。
最開始輸出hello 證實 this爲對象o的
而後輸出hi 證實this是 全局變量
最後輸出hello 證實this被重置爲對象o的
另外

apply有一個孿生兄弟 call,call 和 apply 都是爲了改變某個函數運行時的 context 即上下文而存在的,換句話說,就是爲了改變函數體內部 this 的指向。由於 JavaScript 的函數存在「定義時上下文」和「運行時上下文」以及「上下文是能夠改變的」這樣的概念。
兩者的做用徹底同樣,只是接受參數的方式不太同樣。
JavaScript 中,某個函數的參數數量是不固定的,所以要說適用條件的話,當你的參數是明確知道數量時,用 call,而不肯定的時候,用 apply,而後把參數 push 進數組傳遞進去。當參數數量不肯定時,函數內部也能夠經過 arguments 這個數組來遍歷全部的參數。
 var x = 0;
  function test(){
    alert(this.x);
  }
  var o={};
  o.x = 1;
  o.m = test;
  o.m.apply(); //0

12,typeof 操做符
typeof操做符返回一個字符串,表明一個未被求值的操做對象(unevaluated operand)的類型。
這個在js的反射中用的比較多
值得一說的是 typeof null的值是object而typeof undefined 的值是undefined
並且一下也是須要注意的

typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 儘管NaN是"Not-A-Number"的縮寫,意思是"不是一個數字"
typeof Number(1) === 'number'; // 不要這樣使用!
// 下面的容易使人迷惑,儘可能不要用
typeof new Boolean(true) === 'object';
typeof new Number(1) ==== 'object';
typeof new String("abc") === 'object';

注意
1,正則表達式字面量在某些瀏覽器中不符合標準

typeof /s/ === 'function'; // Chrome 1-12 ... // 不符合 ECMAScript 5.1
typeof /s/ === 'object'; // Firefox 5+ ...    // 符合 ECMAScript 5.1

2,原型鏈中的任何屬性都會產生值

typeof flight.toString // function
typeof flight.constructor// function

咱們有兩種方法去掉這些不須要的屬性,一是在程序中作檢測,丟掉值爲函數的屬性,由於咱們一般項目編碼中須要的是數據而不是函數,第二是使用hasOwnProperty()方法,若是是對象擁有獨有的屬性,會返回true

13,void 操做符
void 運算符會對它的操做數表達式進行求值,而後忽略掉求值的結果,直接返回 undefined

常常會有人用 void(0) 或者 void 0 來代替 undefined 變量來表示 undefined 值,由於他們擔憂本身拿到的 undefined 這個變量的值可能不是 undefined

常常會有人用 void(0) 或者 void 0 來代替 undefined 變量來表示 undefined 值,由於他們擔憂本身拿到的 undefined 這個變量的值可能不是 undefined

void function iife() {
    var bar = function () {};
    var baz = function () {};
    var foo = function () {
        bar();
        baz();
     };
    var biz = function () {};

    foo();
    biz();
}();

當用戶點擊一個以 javascript: 協議開頭的 URI 時,瀏覽器會對冒號後面的代碼進行求值,而後把求值的結果顯示在頁面上,這時頁面基本上是一大片空白,這一般不是咱們想要的。只有當這段代碼的求值結果是 undefined 的時候,瀏覽器纔不會去作這件傻事,因此咱們常常會用 void 運算符來實現這個需求。像下面這樣:

<a href="javascript:void(0);">
  這個連接點擊以後不會作任何事情,若是去掉 void(),
  點擊以後整個頁面會被替換成一個字符 0。
</a>

<a href="javascript:void(document.body.style.backgroundColor='green');">
  點擊這個連接會讓頁面背景變成綠色。
</a>

注意,雖然這麼作是可行的,但利用 javascript: 僞協議來執行 JavaScript 代碼是不推薦的,推薦的作法是爲連接元素綁定 click 事件。

14,yield 操做符
說道yield就不得不提到generator 函數,在ECMAScript 6 引入了generator函數,做用就是返回一個內部狀態的遍歷器,主要特徵是函數內部使用了yield語句。
當調用generator函數的時候,該函數並不執行,而是返回一個遍歷器(能夠理解成暫停執行)。之後,每次調用這個遍歷器的next方法,就從函數體的頭部或者上一次停下來的地方開始執行(能夠理解成恢復執行),直到遇到下一個yield語句爲止,並返回該yield語句的值。

ECMAScript 6草案定義的generator函數,須要在function關鍵字後面,加一個星號。而後,函數內部使用yield語句,定義遍歷器的每一個成員。

function* helloWorldGenerator() {
    yield 'hello';
    yield 'world';
}
var hw = helloWorldGenerator();
hw.next() 
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: undefined, done: true }

hw.next()
// Error: Generator has already finished

yield有點相似於return語句,都能返回一個值。區別在於每次遇到yield,函數返回緊跟在yield後面的那個表達式的值,而後暫停執行,下一次從該位置繼續向後執行,而return語句不具有位置記憶的功能。

上面代碼定義了一個generator函數helloWorldGenerator,它的遍歷器有兩個成員「hello」和「world」。調用這個函數,就會獲得遍歷器。我會在後期《特殊性質與技巧》中使用一個篇幅來介紹yield,它是koa框架實現最核心的理念「減小回調金字塔」必不可少的工具。接下來,咱們將進入次日學習《javascript函數》。

相關文章
相關標籤/搜索