《你不知道的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)
    }
复制代码
相关文章
相关标签/搜索