《你不知道的Javascript--上卷 學習總結》(this和對象)

this全面解析

調用棧

函數調用位置就是函數在代碼中被調用的位置(而不是聲明的位置),尋找調用位置最重要的就是分析調用棧算法

this綁定規則

  • 默認綁定(獨立函數調用 fn)

這個時候的this是window(注意:若是使用嚴格模式,則不能將全局對象用於默認綁定數組

雖然this的綁定規則徹底取決於調用位置,可是隻有函數運行在非strict mode下時,默認綁定才能綁定到全局對象;在嚴格模式下調用函數則不影響默認綁定。安全

function foo(){
        'use strict'
        console.log(this.a)
    }
    
    var a = 2;
    
    foo(); // TypeError : this is undefined
    
    
    function foo(){
        console.log(this.a)
    }
    
    var a = 2;
    
    (function(){
        'use strict'
        
        foo() // 2
    })()
複製代碼
  • 隱式綁定(obj.fn)

調用位置是否有上下文對象,或者說是否被某個對象擁有或者包含,這時候隱式綁定規則會把函數調用中的this綁定到這個上下文對象。bash

function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2,
        foo:foo
    }
    
    obj.foo() // 2
複製代碼

隱式規則會有時候丟失綁定對象。app

一、定義函數別名致使丟失函數

function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2,
        foo:foo
    }
    
    var bar = obj.foo;
    
    var a = 'xx'
    
    bar() // 'xx'  // 這個時候致使this 指向的全局
    
複製代碼

二、當作回調函數ui

function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2,
        foo:foo
    }
    
    var a = 'xx'
    
    setTimeout(obj.foo,100) // 'xx'
複製代碼
  • 顯示綁定(obj.bind/apply/call,bind實現考慮做爲構造函數

傳遞的第一個參數是this對象,若是傳入的是一個原始值來當作this的綁定對象,這個原始值會被轉換成它的對象形式(也就是new String()、new Boolean()或者new Number()),就是裝箱。this

function foo(){
        console.log(this.a)
    }
    
    var obj = {
        a:2
    }
    foo.call(obj) // 2
    
    
    
    function foo(something){
        console.log(this.a,something);
        return this.a + something;
    }
    
    // 輔助函數bind
    function bind(fn,obj){
        return function(){
            return fn.apply(obj,arguments)
        }
    }
    
    var obj = {
        a:2
    }
    
    var bar = bind(foo,obj)
    
    var b = bar(3) ;
    console.log(b) // 5
複製代碼
  • new綁定(var a = new A())

使用new來調用函數,或者說發生構造函數調用時,會自動執行下面的操做。spa

一、建立一個全新的對象。prototype

二、這個新對象會被執行[[prototype]]鏈接

三、這個新對象會綁定到函數調用的this

四、若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象

function foo(a){
        this.a = a;
    }
    
    var bar = new foo(2);
    console.log(bar.a) // 2
複製代碼

優先級

new綁定 > 顯示綁定 > 隱式綁定 > 默認綁定

綁定例外

若是顯示綁定的this傳遞的null或者undefined,實際應用的是默認綁定規則。

建立一個安全的空對象的最簡單的方法就是使用Object.create(null),它建立的空對象不會建立Object.prototype這個委託,因此他比{}更空。

賦值表達式p.foo = o.foo 的返回值是目標函數的引用,所以調用位置是foo()而不是p.foo() 或者 o.foo() ,所以這裏會應用默認綁定。

function foo(){
        console.log(this.a)
    }
    var a = 2;
    var o = {a:3,foo:foo}
    var p = {a:4}
    o.foo() //3
    (p.foo = o.foo)() // 2
複製代碼

對於默認綁定來講,決定this綁定對象並非調用位置是否處於嚴格模式,而是函數體是否處於嚴格模式。若是函數體處於嚴格模式,this會被綁定到undefined,不然this會被綁定到全局。

箭頭函數的this

箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)做用域來決定this。

function foo(){
        return (a) => {
            // this 繼承自foo()
            console.log(this)
        }
    }
    
    var obj1 = {
        a:2
    }
    
    var obj2 = {
        a:3
    }
    
    var bar = foo.call(obj1);
    bar.call(obj2) // 2
複製代碼

對象

類型

主要類型

  • string
  • number
  • boolean
  • null
  • undefined
  • object

內置對象

  • String
  • Number
  • Boolean
  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

內容

一、在對象中,屬性名永遠都是字符串,若是你使用string(字面量)之外的其餘值做爲屬性名,那它首先會被轉換爲一個字符串,即便是數字也不例外。

二、數組也是對象,因此雖然每一個下標都是整數,你仍然能夠給數組添加屬性.可是數組的length值並未發生改變

三、若是你試圖向數組添加一個屬性,可是屬性名是一個數字,那麼就會變成數值下標(所以會修改數組的內容而不是添加一個屬性)

var myArray = ['foo',42,"bar"];
    myArray.baz = "baz";
    myArray.length // 3
    myArray.baz // "baz"
複製代碼

屬性描述符(數據描述符)

對象默認的屬性描述符

var myObject = {
        a:2
    }
    Object.getOwnPropertyDescriptor(myObject,a)
    
    /*
        {
            value:2,
            writable:true, // 可寫
            configurable:true, // 可配置
            enumerable:true  // 可枚舉
        }
    
    */
複製代碼

本身設置屬性描述符

var myObject = {
    }
    
    Object.defineProperty(myObject,a,{
        value:2,
        writable:true, 
        configurable:true, 
        enumerable:true 
    })
複製代碼

writable

決定是否能夠修改屬性的值,true爲能夠,false爲不能夠

configurable

決定屬性是否可配置,若是經過defineProperty定義一個屬性configurable爲false,則不能夠在進行配置(defineProperty),configurable:false還會禁止刪除這個屬性

注意:即便屬性是configurable:false ,咱們仍是能夠把writable的狀態由true改成false,可是沒法由false改成true

enumerable

控制屬性是否會出如今對象的屬性枚舉中,好比說for..in循環。

不變性

一、對象常量

結合writable:false和configurable:false就能夠建立一個真正的常量屬性(不可修改、重定義或者刪除)

二、禁止擴展

若是想禁止一個對象添加新屬性而且保留已有屬性,可使用Object.preventExtensions()

Object.preventExtensions(myObject) 
複製代碼

三、密封 Object.seal()(Object.preventExtensions加全部屬性標記爲configurable:false)

四、凍結 Object.freeze() (Object.seal加把全部的"數據訪問"屬性標記爲writable:false)

[[Put]]

[[Put]]被觸發時,實際的行爲取決於許多因素,包括對象中是否已經存在這個屬性(這是最重要的因素),若是已經存在這個屬性,[[Put]]算法大體會檢查下面這些內容。

一、屬性是不是訪問描述符?若是是而且存在setter就調用setter。

二、屬性的數據描述符中writable是否爲false?若是是,在嚴格模式下靜默失敗,在嚴格模式下拋出TypeError異常。

三、若是都不是,將該值設置爲屬性的值。

Getter和Setter

在ES5中可使用getter和setter部分改寫默認操做,可是隻能應用在單個屬性上,沒法應用在整個對象上。getter是一個隱藏函數,會在獲取屬性時調用。setter也是一個隱藏函數,會在設置屬性時調用

當你給一個屬性定義getter、setter或者二者都有時,這個屬性會被定義爲"訪問描述符"(和"數據描述符"相對)。對於訪問描述符來講,Javascript會忽略它們的value和writable特性,取而代之的是關心set和get特性。

var myObject = {
        // 給a定義一個getter
        get a() {
            return 2
        }
    }
    
    Object.defineProperty(
        myObject,
        "b",
        {
            get:function(){
                return this.a *2
            },
            enumerable:true
        }
    )
    myObject.a = 3  // 沒有設置成功 由於沒有set,因此通常get和set要成對出現
    myObject.a // 2
    myObject.b // 4
    
複製代碼

存在性

in操做符會檢查屬性是否在對象及其[[Prototype]] 原型鏈中。(實際上檢查的是某個屬性名是否存在)

hasOwnProperty只會檢查屬性是否在myObject對象中,不會檢查[[Prototype]]鏈。

enumerable:true 就至關於"能夠出如今對象屬性的遍歷中"。

propertyIsEnumerable會檢查給定的屬性名是否直接存在於對象中(而不是在原型鏈上)而且知足enumerable:true。

Object.keys會返回一個數組,包含全部可枚舉屬性。

Object.getOwnPropertyNames會返回一個數組,包含全部屬性,不管他們是否可枚舉。

in和hasOwnProperty的區別在因而否查找[[Prototype]]鏈。

object.keys和object.getOwnPropertyNames都只會查找對象直接包含的屬性。

遍歷

若是想直接遍歷數組的值可使用for..of循環語法。(若是對象自己定義了迭代器的話也能夠遍歷對象)

for..of循環首先會向被訪問對象請求一個迭代器對象,而後經過調用迭代器對象的next()方法來遍歷全部返回值。

var myArray = [1,2,3]
    for(var v of myArray){
        console.log(v) // 1,2,3
    }
複製代碼

數組有內置的@@iterator,所以for..of能夠直接應用在數組上。咱們使用內置的@@iterator來手動遍歷數組,

var myArray = [1,2,3];
    var it = myArray[Symbol.iterator]();
    
    it.next() // {value:1,done:false}
    it.next() // {value:2,done:false}
    it.next() // {value:3,done:false}  
    it.next() // {done:true}   value爲當前的遍歷值   done是一個布爾值,表示是否還有能夠遍歷的值
複製代碼

普通的對象如何實現for..of遍歷(須要本身定義Symbol.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)
    }
複製代碼
相關文章
相關標籤/搜索