You Don't Know JS: this & Object Prototypes( 第3章 对象)

前2章探索了this绑定指向不同的对象需要函数引用的call-site。

但是什么是对象,为什么我们需要指向它们?

本章探索细节。


 

Syntax

 the rules  that describe how words and phrases are used in a computer language!

对象来源于两种forms

  •  declarative(literal) form:   
  •  constructed from
var myObj = {
    key: value
    // ...
};

var myObj = new Object();
myObj.key = value;

2者的结果没有区别。

用constructed form来创建对象及其少见。 开发者总是使用literal form。就连内建对象也是这样。

 


 

 

Type

object是6种primary types之一。 (string, number, boolean, null, undefined, object) 

⚠️, 不是everything in JavaScript is an object, 这句话在JavaScript中完全❌!

 

对象有复杂的子类型。 complex primitives。

  • 函数是一种子类型,a callable object。不过和普通的对象使用方式是一样的。
  • 数组Array也是一种子类型,有额外的行为。
  • 普通对象是hash类型。是对象的主类型。

 

Build-in 对象

其他的对象子类型,都是内建对象。

从名字看他们似乎和他们的simple primitives counter-parts(副本)相关,但是:

他们的关系是很复杂的,下面会进行简短的探索。

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

👆都是对象!!! 不要看名字String,就认为它是string。

 类似其他语言的类,比如Ruby的String类。

但是在JS, 他们是内建函数。可以当作一个constructor使用(new Xxxx, 见上章)。

结果会得到一个这个子类型的新的constructed object。

 

例子:一个primary type: string

var str = "hello"typeof str
//"string"
str instanceof String
//false

 

对比⬆️例子:这是一个对象,由String对象构建 

var strObject = new String( "I am a string" );
typeof strObject;                                 // "object"
strObject instanceof String;                    // true

 就是一个对象,可以使用对象的方法: strObject['key'] ,但只能读,不能改!!

 

理解:

实际上JS团体并不怎么使用这个方式创建这个对象子类型。它没有literal form

因为literal form的创建方式,基本可以替代constructed object form。比如使用length方法,通过charAt(3)得到对应的value.

var strPrimitive = "I am a string";

console.log( strPrimitive.length );            // 13

console.log( strPrimitive.charAt( 3 ) );    // "m"

甚至strPrimitive["3"] 的写法也能得到值:m。 

原因是engine自动强迫它成为一个String 对象,所以property/method都可以用

这叫做same sort of coercion相同类型强制转化。

string, number ,boolean都会被类型强制转化,然后就可以使用方法和特性了。

 

Datal 值,只能用constructed object form形式创建,它没有literal form副本。

null, undefined没有对象包裹器形式,只有primitive values。

 

Objects, Arrays, Functions, and RegExps (regular expressions) are all objects。

提供了constructed form。这种形式比literal form有更多的可选项。

大多时候使用更简便的literal form。除非你需要额外的设置options。

 

Error对象很少直接用,一般是在错误发生时自动抛出。可以通过new Error()创建,很少用到。



 

 

Contents(大章节)

一个对象的contents由values and type组成,储存在指定的地点,叫做properties。

一般我们提到contents ,暗示了这些values被储存在这个对象内,但这只是表象。

实际上,engine stores values in implementation-dependent ways,并不是储存值在一些对象容器内。

储存在container的只是属性名字。作为一个pointers。指向标记。

var myObject = {
    a: 2
};

myObject.a;        // 2

myObject["a"];    // 2

 

使用点或方括号["..."],的结果一样,但用法有区别。

  • 点后面需要一个identifier
  • ["..."]内部是一个字符串。因此可以使用如“Super-Funn!”这样的复杂写法。

在对象中,属性名字总是strings.如果你使用number,boolean作为属性名字,它会被转化为string.

 


 

Computed Property Names

 ES6增加了可以计算的属性名字,你可以设置一个表达式,用一个[]包裹这个表达式。

var prefix = "foo";
 
var myObject  = {
    [prefix + "bar"]: "hello",
    [prefix + "baz"]: "world"
}
 
myObject.foobar         //"hello"
myObject["foobaz"]      //"world"

 

 


 

Property vs. Method

property access。是一些开发者用于区别的名称。

在其他语言,函数是属于对象(类)的,被称为方法method。

The safest conclusion is probably that "function" and "method" are interchangeable in JavaScript.

函数,方法,其实没什么区别。

 

即使你声明一个函数表达式作为这个对象的一部分,实际上这个函数并不属于这个对象。仅仅是引用

var myObject = {
    foo: function foo() {
        console.log( "foo" );
    }
};

var someFoo = myObject.foo;

someFoo;        // function foo(){..}

myObject.foo;    // function foo(){..}

 

 


 

Arrays

数组默认有数字索引。

var myArray = [ "foo", 42, "bar" ];

myArray.length;        // 3

myArray[0];            // "foo" ,方括号内是数字,不是字符串!!

myArray[2];            // "bar"

 

数组也是对象,所以可以添加属性给数组 

 

 


 

Duplicating Objects(没看懂!)

最常见的请求功能之一:如何复制一个对象!

浅层复制,深层复制。

深层复制容易出现无限循环。

 

可以使用Object.assign()方法。

 


 

Property Descriptors

 

用于对象的属性的特点的描述。Object.getOwnPropertyDescriptor( 一个对象名, '这个对象的属性名字')

var myObject = {
    a:2,
}
 
Object.getOwnPropertyDescriptor(myObject, "a")
//输出 {value: 2, writable: true, enumerable: true, configurable: true}

 

你可以看到,这个属性描述器。 属性a包括了除了value之外,还是其他3个特性。

 

使用Object.defineProperty()方法,可以新建一个属性,或者修改现存的属性的内部特性。一般用不上。

 

特性:Writable

严格模式下,会报错; 非严格模式不会提示❌。

"use strict";

var myObject = {};

Object.defineProperty( myObject, "a", {
    value: 2,
    writable: false, // not writable!
    configurable: true,
    enumerable: true
} );

myObject.a = 3; // TypeError,

//Cannot assign to read only property 'a' of object 

 

 

特性:Configurable

 设置了false的话,就不能再使用defineProperty()方法修改了。

 configurable: false代表你不能对这个属性进行特性修改

 同时,这个属性也不能被删除!(使用: delete myObject.a 直接删除对象的属性

 

特性:Enumerable

这个特性控制是否一个属性可以show up in certain object-property enumerations。(使用一些计算相关的方法) 


 

Immutability不变

 

有时候,想要让对象或对象的属性不会发生改变,冻结freeze!

ES5增加了相关方法。

⚠️

所有方法只创建浅层不变(冻结),如果 一个对象有一个引用到另一个对象, 这个对象的contents不受冻结的影响。

 

Object Constant

使用writable: false和configurable: false,你可以创建一个常量(不能修改、删除)作为一个对象属性。

 

Prevent Extensions

设置一个对象不能新增属性(可以操作旧属性): 调用Object.preventExtensions( 一个对象);

 

Seal

Object.seal(),让一个对象调用Object.preventExtensions(),并且所有属性都是configurable:false。

即不能删除已有属性,不能新增属性。 

 

Freeze

Object.freeze() ,最高层次的冻结。相当于上面三个的全部效果。不可修改,删除属性 ,不能新增属性。


 

存取一个对象属性值的内部运行机制!

 

[[Get]]

 细微的但重要的细节,关于proterty access被如何执行!

 这个操作符号执行类似一个函数的调用: [[Get]]().它会查找属性,并返回对应的值。

它会使用prototype chain进行查找。

如果找不到相关property's name,会返回undefined

 

[[Put]]

有[[Get]] operation用于从一个属性中得到一个value,自然也有对应的[[Put]] operation!

 也是类似调用[[Put]](),有一系列判断!

判断属性是否存在:

  • 属性如果存在则:进一步检查:
  1. 属性是否是一个存取描述器? 是的话,调用setter。
  2. 属性是否是一个数据描述器,并且writeable: false? 是的话,在非严格模式失败,或者在严格模式抛出❌TypeError.
  3. 其他情况,设置value给现存的属性。
  • 属性不存在:这个判断的过程更复杂,会用到[[Prototypte]]

 

Getters & Setters

默认的[[Put]] and [[Get]]操作,可以完全控制对象,从已存在的属性取值,把值设置给新的属性或已存在属性。

 

ES5介绍了一个方法,可以重写这2个默认操作的部分代码,通过使用getters and setters,在a pre-property level。

Getters是属性可以调用一个隐藏的函数来取出一个值。

Setters是属性可以实际调用一个隐藏的函数来设置一个值。

 

当你定义一个属性时,有getter,或者setter。 它的定义就成为了一个'accessor descriptor'存取描述器。

(相反的是‘data descriptor’数据描述器)。

当属性是存取描述器时,value和writable特性就无意义并忽略了。作为替代,JS会改用set, get特性 。

 

Vue 会在初始化实例时对属性执行 getter/setter 转化过程。让属性成为响应式,即监听setter是否被调用。

 

可以使用2个方法定义对象的属性为accessor descriptor:

  • 通过对象字面量句法:get a() { .. }
  • 通过明确的定义:使用defineProperty(..)
var myObject = {
    get a() {
        return this._a_;
    },

    set a(val) {
        this._a_ = val * 2;
    }
};

myObject.a = 2;

myObject.a; // 4

 

 


 

Existence

 如何确定一个对象中有一个属性,2种方法: 

  1. in 关键字, 检查prototype chain
  2. hasOwnProperty()方法,  不检查原型链。
var myObject = {
    a: 2
};

("a" in myObject);                // true
("b" in myObject);                // false

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

 in关键字,会检查属性是否在对象中存在,或者是否存在于高层的原型链中。

 

 

Enumeration

对象的属性的特性之一。这里进一步讨论它。

var myObject = { };

Object.defineProperty(
    myObject,
    "a",
    // make `a` enumerable, as normal
    { enumerable: true, value: 2 }
);

Object.defineProperty(
    myObject,
    "b",
    // make `b` NON-enumerable
    { enumerable: false, value: 3 }
);

myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty( "b" ); // true

// .......

for (var k in myObject) {
    console.log( k, myObject[k] );
}
// "a" 2

 

可以看出,myObject.b存在可存取,但是不能用在foo..in循环。

所以enumerable的基本意思就是 “如果这个对象的属性被iterated通过,就会be included”。

 

好的习惯:

使用for .. in循环在对象上(返回属性/value对儿);

使用传统的for循环在数组上, 用于返回array的值。

 

相关

对象.propertyIsEnumerable("a") 方法。

Object.keys(..) , 返回当前对象的enumerabled的属性名字,格式是array.

Object.getOwnPropertyNames(..),  返回当前对象的所有属性名字。

 

 


 


 

 

Iteration

数字索引的数组:用标准循环。循环数组的index.

var myArray = [1, 2, 3];

for (var i = 0; i < myArray.length; i++) {
    console.log( myArray[i] );
}
// 1 2 3

 

 

数组的循环方法。

ES5新增:forEach(), every(), some(). 都可以接受一个回调函数。区别是他们如何各自响应从回调中返回的value.

 

forEach():

循环数组的所有value!,  忽略从回调中返回的value。

var numbers = [65, 44, 12, 4];
function myFunction(item,index,arr) { arr[index] = item * 10 } numbers.forEach(myFunction)
//输出:undefined 。 forEach不返回value.

numbers //输出:(
4) [650, 440, 120, 40]

 

 

every(..),循环value,发现回调函数返回了一个false(falsy)的值停止循环,否则当循环结束,返回true。

some(), 正相反,当发现回调函数返回了一个true(truthy)的值停止循环, 否则,当循环结束, 返回false。

 

every(), some()相当于for循环内,当查到满足条件的value时,执行break语句。

 

之后还有一大块内容:深度循环内部@@iterator(没看),涉及ES6

 


Review (TL;DR)

对象声明有literal form, a constructed form两种 。

错误概念,JS中的每个事都是对象❌。

对象有子类型:如function。

对象是key/value的集合。通过.propName, ["propName"]存取。

当一个属性被存取时,Engine会使用内部默认的[[Get]] operation (and [[Put]]for setting values), 它们不光查看对象上的属性,也会检索原型链条。

属性有确定的特性,特性可以通过property descriptor来控制。如writable, configurable。

对象可以通过多种方法使用这些特性,如Object.preventExtensions(), Object.freeze()等。

Properties无需contain values,它们可以通过getters/setters设置(存取属性)。它们可以被enumerable或不能,例如:enumerable控制属性是否可以用for..in循环它们。

 

posted @ 2018-10-05 19:56  Mr-chen  阅读(177)  评论(0编辑  收藏  举报