JavaScript中并非一切皆对象

对象是js中的基础以及核心,在js中有六种主要类型:string  number  boolean null undefined object

除了oject类型以为其他五种本身并非对象,null本身被划为object类型本身是js中的一个bug,即使它的typeof类型是 object,实际上null本身还是基本类型。

常见的错误说法是:js中万物皆是对象,这显然是错误的。

实际上,js中有许多特殊的对象子类型。我们称之为复杂基本类型。

js中有一些对象子类型被称为内置对象,如:

• String

• Number

• Boolean

• Object

• Function

• Array

• Date

• RegExp

• Error

原理是这样的,不同的对象在底层都表示为二进制,在 JavaScript 中二进制前三位都为 0 的话会被判 断为 object 类型,null 的二进制表示是全 0,自然前三位也是 0,所以执行 typeof 时会返回“object”。

在 JavaScript 中,它们实际上只是一些内置函数。这些内置函数可以当作构造函数来使用,从而可以构造一个对应子类型的新对象。举例来说:

var strPrimitive = "I am a string";

 typeof strPrimitive; // "string" 

strPrimitive instanceof String; // false

var strObject = new String( "I am a string" ); 

typeof strObject; // "object"

strObject instanceof String; // true

// 检查 sub-type 对象
Object.prototype.toString.call( strObject ); // [object String]

数组也是对象,所以虽然每个下标都是整数,你仍然可以给数组添加属性:

var myArray = [ "foo", 42, "bar" ]; 
myArray.baz = "baz";
myArray.length; // 3
myArray.baz; // "baz"

可以看到虽然添加了命名属性,数组的 length 值并未发 生变化。

如果你试图向数组添加一个属性,但是属性名“看起来”像一个数字,那它会变成 一个数值下标(因此会修改数组的内容而不是添加一个属性):

var myArray = [ "foo", 42, "bar" ];
myArray["3"] = "baz"; 
myArray.length; // 4
myArray[3]; // "baz"  

接着有必要介绍行对象的三个属性:writable(可写)、 enumerable(可枚举)和 configurable(可配置)。

var myObject = {};
Object.defineProperty( myObject, "a", { 
    value: 2,
    writable: true, 
    configurable: true,
    enumerable: true
} )
writable 决定是否可以修改属性的值。
configurable 禁止修改以及删除属性。delete也不好使。
enumerable 决定对象的属性是否可以被枚举,如for in

Object.seal(..) 会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions(..) 并把所有现有属性标记为 configurable:false。

Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们 的值。

对象的defineproperty以及set,get,在vue等框架中是核心原理。这里就不展开说了,所以vue只能兼容到IE8,因为必须的得支持对象的es5对象中的一系列方法才行。

对于上面提到的for in 可以联想到for of,还有重要的iterator数组的迭代器。如何手动调试循环 next()启动,以及返回值是什么,看下面的例子。

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            

调用迭代器的 next() 方法会返回形式为 { value: .. , done: .. } 的值, value 是当前的遍历值,done 是一个布尔值,表示是否还有可以遍历的值。

最后的调用一次 next() 才能得到 done:true,从而确定完成遍历。

和数组不同,普通的对象没有内置的 @@iterator,所以无法自动完成 for..of 遍历。之所 以要这样做,有许多非常复杂的原因,不过简单来说,这样做是为了避免影响未来的对象 类型。

for..of 循环每次调用 myObject 迭代器对象的 next() 方法时,内部的指针都会向前移动并 返回对象属性列表的下一个值(再次提醒,需要注意遍历对象属性 / 值时的顺序)。

原文地址:https://www.cnblogs.com/hjj2ldq/p/9310513.html