《你不知道的javascript》讀書筆記

上卷

顯示綁定:css

JavaScript 中的「全部」函數都有一些有用的特性(這和它們的 [[Prototype]] 有關——以後咱們會詳細介紹原型),能夠用來解決這個問題。具體點說,可使用函數的 call(..) 和apply(..) 方法。嚴格來講,JavaScript 的宿主環境有時會提供一些很是特殊的函數,它們並無這兩個方法。可是這樣的函數很是罕見,JavaScript 提供的絕大多數函數以及你自己建立的全部函數均可以使用 call(..) 和 apply(..) 方法。數組

這兩個方法是如何工做的呢?它們的第一個參數是一個對象,是給 this 準備的,接着在調用函數時將其綁定到 this。由於你能夠直接指定 this 的綁定對象,所以咱們稱之爲顯式綁定。數據結構

思考下面的代碼:app

function foo() { 
 console.log( this.a ); 
} 
var obj = { 
 a:2 
}; 
foo.call( obj ); // 2

經過 foo.call(..),咱們能夠在調用 foo 時強制把它的 this 綁定到 obj 上。ide

用call來實現繼承函數

        var test = {
            name: 'He',
            age: 25
        }
var test2 = { write: function () { console.log(this.name) }, writeAge: function () { console.log(this.age) } }
test2.write.call(test)
//He test2.writeAge.call(test) //25 test2.write(); //undefined test2.writeAge(); //undefined

判斷this

如今咱們能夠根據優先級來判斷函數在某個調用位置應用的是哪條規則。能夠按照下面的順序來進行判斷:

1. 函數是否在 new 中調用(new 綁定)?若是是的話 this 綁定的是新建立的對象。
  var bar = new foo()ui


2. 函數是否經過 call、apply(顯式綁定)或者硬綁定調用?若是是的話,this 綁定的是指定的對象。
  var bar = foo.call(obj2)this


3. 函數是否在某個上下文對象中調用(隱式綁定)?若是是的話,this 綁定的是那個上下文對象。
  var bar = obj1.foo()spa


4. 若是都不是的話,使用默認綁定。若是在嚴格模式下,就綁定到 undefined,不然綁定到全局對象。
  var bar = foo()  prototype

 

存在性  

        var myobject={
      //...
        }
        myobject.a //undefined

  訪問myobject對象的a屬性,返回的值爲 undefined,可是這個值有多是屬性中存儲的 undefined,也多是屬性不存在返回的 undefined,那麼如何區分這兩種狀況呢?

  咱們能夠在不訪問屬性值的狀況下判斷對象中是否存在這個屬性:

var myObject = { 
 a:2 
}; 
(
"a" in myObject); // true ("b" in myObject); // false

myObject.hasOwnProperty( "a" ); // true myObject.hasOwnProperty( "b" ); // false

  in 操做符會檢查屬性是否在對象及其 [[Prototype]] 原型鏈中,hasOwnProperty(..) 只會檢查屬性是否在 myObject 對象中,不會檢查 [[Prototype]] 鏈。

  看起來in像是檢測容器內是否存在某個值,可是實際上檢查的是某個屬性名是否存在。  

4 in [2,4,6] //false

  數組[2,4,6]的屬性名是0,1,2  由於檢測的是屬性名, 因此會輸出false

 

//// 數組有內置的 @@iterator,所以 for..of 能夠直接應用在數組上。咱們使用內置的 @@iterator 來手動遍歷數組,看看它是怎麼工做的
        var myArr = [1, 2, 3];
        var it = myArr[Symbol.iterator]();
        it.next(); // { value:1, done:false } 
        it.next(); // { value:2, done:false } 
        it.next(); // { value:3, done:false } 
        it.next(); // { done:true }    

如你所見,調用迭代器的 next() 方法會返回形式爲 { value: .. , done: .. } 的值,value 是當前的遍歷值,done 是一個布爾值,表示是否還有能夠遍歷的值。
注意,和值「3」一塊兒返回的是 done:false,乍一看好像很奇怪,你必須再調用一次next() 才能獲得 done:true,從而肯定完成遍歷。這個機制和 ES6 中發生器函數的語義相關,不過已經超出了咱們的討論範圍。
和數組不一樣,普通的對象沒有內置的 @@iterator,因此沒法自動完成 for..of 遍歷。之因此要這樣作,有許多很是複雜的緣由,不過簡單來講,這樣作是爲了不影響將來的對象類型。

固然,你能夠給任何想遍歷的對象定義 @@iterator,舉例來講:

var myObject = { 
 a: 2, 
 b: 3 
}; 
Object.defineProperty( myObject, Symbol.iterator, { 
 enumerable: false, 
 writable: false, 
 configurable: true, 
 value: function() { 
 var o = this; 
 var idx = 0; 
 var ks = Object.keys( o ); 
 return { 
 next: function() { 
 return { 
 value: o[ks[idx++]], 
 done: (idx > ks.length) 
 }; 
 } 
 }; 
 } 
} ); 
// 手動遍歷 myObject 
var it = myObject[Symbol.iterator](); 
it.next(); // { value:2, done:false } 
it.next(); // { value:3, done:false } 
it.next(); // { value:undefined, done:true }
// 用 for..of 遍歷 myObject 
for (var v of myObject) { 
 console.log( v ); 
} 
// 2 
// 3

咱們使用 Object.defineProperty(..) 定義了咱們本身的 @@iterator(主要是爲了讓它不可枚舉),不過注意,咱們把符號看成可計算屬性名(本章以前有介紹)。此外,也能夠直接在定義對象時進行聲明,好比 var myObject = { a:2, b:3, [Symbol.iterator]: function() { /* .. */ } }。

for..of 循環每次調用 myObject 迭代器對象的 next() 方法時,內部的指針都會向前移動並返回對象屬性列表的下一個值(再次提醒,須要注意遍歷對象屬性 / 值時的順序)。

對象章--小結

一、對象就是鍵 / 值對的集合。能夠經過 .propName 或者 ["propName"] 語法來獲取屬性值。訪問屬性時,引擎實際上會調用內部的默認 [[Get]] 操做(在設置屬性值時是 [[Put]]),[[Get]] 操做會檢查對象自己是否包含這個屬性,若是沒找到的話還會查找 [[Prototype]]鏈(參見第 5 章)。

二、屬性的特性能夠經過屬性描述符來控制,好比 writable 和 configurable。此外,可使用Object.preventExtensions(..)、Object.seal(..) 和 Object.freeze(..) 來設置對象(及其屬性)的不可變性級別

三、屬性不必定包含值——它們多是具有 getter/setter 的「訪問描述符」。此外,屬性能夠是可枚舉或者不可枚舉的,這決定了它們是否會出如今 for..in 循環中。

四、你可使用 ES6 的 for..of 語法來遍歷數據結構(數組、對象,等等)中的值,for..of會尋找內置或者自定義的 @@iterator 對象並調用它的 next() 方法來遍歷數據值。

 

對象關聯

 

        var foo2 = {
            name: 'He',
            sayName: function () {
                console.log(this.name)
            }
        }

        var foo2_n = Object.create(foo2);
        foo2_n.sayName();

 

委託模式

 

        Task = {
            setId: function (id) {
                this.id = id
            },
            outputId: function () {
                console.log(this.id)
            }
        }
        // 讓 XYZ 委託 Task 
        xyz = Object.create(Task);
        xyz.prepareTask = function (id, label) {
            this.setId(id);
            this.label = label
        }
        xyz.outputTaskDetails = function () {
            this.outputId();
            console.log(this.label)
        }
        // xyz.prepareTask(1,'委託')

 

對象委託關聯

        Foo = {
            init: function (who) {
                this.me = who
            },
            identify: function () {
                return 'I am ' + this.me + '.'
            }
        }
        Bar = Object.create(Foo);
        Bar.speak = function () {
            console.log("Hello, " + this.identify())
        }
        var a1 = Object.create(Bar);
        a1.init('A1');
        var b1 = Object.create(Bar);
        b1.init('B1');
        a1.speak();
        b1.speak();

控件 「類」

// 父類
        function widget(width, height) {
            this.width = width || 50;
            this.height = height || 50;
            this.$elem = null;

        }
        widget.prototype.render = function ($where) {
            if (this.$elem) {
                this.$elem.css({
                    width: this.width + 'px',
                    height: this.height + 'px'
                }).appendTo($where);
            }
        }
        //子類
        function Button(width, height, label) {
            widget.call(this, width, height);
            this.label = label || "default";
            this.$elem = $('<button>').text(this.label);
        }
        // 讓Button繼承widget
        Button.prototype = Object.create(widget);
        // 重寫render方法
        Button.prototype.render = function ($where) {
            widget.prototype.render.call(this, $where);
            this.$elem.click(this.onClick.bind(this));
        }
        Button.prototype.onClick = function (evt) {
            console.log("Button " + this.label + " clicked!")
        }
        $(function () {
            var $body = $(document.body);
            var button1 = new Button(70, 70, '按鈕1');
            var button2 = new Button(90, 90, '按鈕2');
            button1.render($body);
            button2.render($body);
        })

使用對象關聯風格委託來更簡單地實現 Widget/Button

        var widget2={
            init:function(width,height){
                this.width=width||50;
                this.height=height||50;
                this.$elm=null;
            },
            insert:function($where){
                if(this.$elm){
                    this.$elm.css({
                        width:this.width+'px',
                        height:this.height+'px',
                        marginLeft:'30px'
                    }).appendTo($where)
                }
            }
        }
        var Button2=Object.create(widget2);
        Button2.setup=function(width,height,label){
            this.init(width,height);
            this.label=label||"default";
            this.$elm=$("<button>").text(this.label);
        }
        Button2.build=function($where){
            this.insert($where);
            this.$elm.click(this.onClick.bind(this));
        }
        Button2.onClick=function(){
            console.log("Button "+this.label+" clicked");
        }
        $(document).ready(function(){
        var body=$(document.body);
        var button1=Object.create(Button2);
        var button2=Object.create(Button2);
        button1.setup(80,80,'取消')
        button1.build(body)
        button2.setup(80,80,'肯定!');
        button2.build(body);
        })

 中卷

 

ES5 規範 9.2 節中定義了抽象操做 ToBoolean,列舉了布爾強制類型轉換全部可能出現的
結果。
如下這些是假值:
• undefined
• null
• false
• +0、-0 和 NaN
• ""
假值的布爾強制類型轉換結果爲 false。

一元運算符 ! 顯式地將值強制類型轉換爲布爾值。可是它同時還將真值反轉爲假值(或者將假值反轉爲真值)。因此顯式強制類型轉換爲布爾值最經常使用的方法是 !!,由於第二個 ! 會將結果反轉回原值

var a = "0";
var b = [];
var c = {};
var d = "";
var e = 0;
var f = null;
var g;
!!a; // true
!!b; // true
!!c; // true
!!d; // false
!!e; // false
!!f; // false
!!g; // false

 優先級

var a = 42;
var b = "foo";
var c = false;
var d = a && b || c ? c || b ? a : c && b : a;
d; // 42

 運算符優先級    && > || > ? :

 

 

利用優先級將代碼分解:

((a && b) || c) ? ((c || b) ? a : (c && b)) : a

如今來逐一執行

(1) (a && b) 結果爲 "foo"。
(2) "foo" || c 結果爲 "foo"。
(3) 第一個 ? 中,"foo" 爲真值。
(4) (c || b) 結果爲 "foo"。
(5) 第二個 ? 中,"foo" 爲真值。
(6) a 的值爲 42。
所以,最後結果爲 42。

 

js類型檢測:https://www.imooc.com/video/5677

typeof 適合基本類型和函數對象的判斷 不適用array等特殊類型

        typeof 100;//number
        typeof true;//boolean
        typeof function(){};//function
        typeof (undefined);//undefined
        typeof new Object;//Object
        typeof [1,2];//Object
        typeof NaN;//number
        typeof null;//Object

instanceof 經常使用於判斷對象類型,它是基於原型鏈去判斷的操做符 

(obj instanceof Object)

instanceof它但願左操做數(obj)是一個對象,若是不是對象,是基本數據類型(好比 string,number),直接返回false。

它但願右操做數必須是一個函數對象或者函數構造器,若是不是會拋出異常Type Error。

instanceof大概原理:判斷左操做數對象的原型鏈上是否有右構造函數的prototype屬性

[1,2] instanceof Array;//true
new Object() instanceof Array; //false

 

 

相關文章
相關標籤/搜索