重读Javascript之Object

Object

对象是Javascript中最常的内置对象之一。除了null 和 undefined,其他的所有的都可以转换为对象。可以把对象看成含有键值一种数据结构,键称为对象的属性(只能是数字或者字符串),而值可以其他的任何类型。

例子

  • 对象是永远引用类型

//对象是永远引用类型,对象名指向内存中的某个位置,而不是复制值。
var studentA = {
    name: "sundway"
}
var studentB = studentA;
studentB.name = "modify";
console.log(studentA.name);        // "modify"
console.log(studentB.name);        // "modify"

/*studentA和studentB同时是对内存中的一个引用,所以studentB.name的更改是对studentB.name和studentA.name的共同更改*/
  • 对象的属性只能是字符串或者数字。

var studentA = {
    "name": "sundway" //“name”可写成name
}
var studentA = {
    1 : "sundway"
}
console.log(studentA[1]); // 输出"sundway"。
/*当属性为数字时,不能通过.运算符去读取属性值,只能通过[]去读取属性值。*/
  • Javascript所有的都可以表现的像对象,但只有null 和 undefined比较特殊。

/*经常会有这样的误解,数字字面量不能当作对象使用。其实主要原因Javascript解析器将数字后面和.运算符当成浮点类型的字面量。*/
2.toString(); // Uncaught SyntaxError
2..toString(); // the second point is correctly recognized
2 .toString(); // note the space left to the dot
(2).toString(); // 2 is evaluated first

属性

属性描述符:属性描述符有两种主要形式,数据描述符(configurable、enumerable、value、writable)和存取描述(configurable、enumerable、get、set)符。数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。描述符必须是两种形式之一;不能同时是两者。(注:polymer和vue等库底层貌似通过get和set实现双向绑定)

Object.prototype

Object.prototype是一个原型对象,在JavaScript中,所有的对象都是基于 Object;所有的对象都继承了Object.prototype的属性和方法,它们可以被覆盖(除了以null为原型的对象,如 Object.create(null));

Object.prototype.constructor

返回一个指向创建了该对象原型的函数引用。需要注意的是,该属性的值是那个函数本身,而不是一个包含函数名称的字符串。

Object.prototype.__proto__

一个对象的proto 属性和自己的内部属性[[Prototype]]指向一个相同的值 (通常称这个值为原型),原型的值可以是一个对象值也可以是null(比如说Object.prototype.proto的值就是null).该属性可能会引发一些错误,因为用户可能会不知道该属性的特殊性,而给它赋值,从而改变了这个对象的原型. 如果需要访问一个对象的原型,应该使用方法Object.getPrototypeOf.

方法

Object.create

ecma-262规范:

Object.create ( O [ , Properties ] )

The create function creates a new object with a specified prototype. When the create function is called, the following steps are taken:

  • If Type(O) is neither Object nor Null, throw a TypeError exception.

  • Let obj be ObjectCreate(O).

  • If the argument Properties is present and not undefined, then
    Return ObjectDefineProperties(obj, Properties).

  • Return obj.

Polyfill (基于Object.prototype.hasOwnProperty。)
if(typeof Object.create != 'function'){
    Object.create = (function(){
        //为了节省内存,使用一个共享的构造器
        function Temp() {};
        
        // 使用 Object.prototype.hasOwnProperty 更安全的引用 
        var hasOwn = Object.prototype.hasOwnProperty;
        
        return function (O) {
          // 1. 如果 O 不是 Object 或 null,抛出一个 TypeError 异常。
          if (typeof O != 'object') {
            throw TypeError('Object prototype may only be an Object or null');
          }

          // 2. 使创建的一个新的对象为 obj ,就和通过
          //    new Object() 表达式创建一个新对象一样,
          //    Object是标准内置的构造器名
          // 3. 设置 obj 的内部属性 [[Prototype]] 为 O。
          Temp.prototype = O;
          var obj = new Temp();
          Temp.prototype = null; // 不要保持一个 O 的杂散引用(a stray reference)...

          // 4. 如果存在参数 Properties ,而不是 undefined ,
          //    那么就把参数的自身属性添加到 obj 上,就像调用
          //    携带obj ,Properties两个参数的标准内置函数
          //    Object.defineProperties() 一样。
          if (arguments.length > 1) {
            // Object.defineProperties does ToObject on its first argument.
            var Properties = Object(arguments[1]);
            for (var prop in Properties) {
              if (hasOwn.call(Properties, prop)) {
                obj[prop] = Properties[prop];
              }
            }
          }

          // 5. 返回 obj
          return obj;
        };
    })()
}

Object.is()

Object.is()[ES6]方法用来判断两个值是否是同一个值。

ecma-262规范:

Object.is ( value1, value2 )
When the is function is called with arguments value1 and value2 the following steps are
taken:

  • Return SameValue(value1, value2).

以上规范中最终返回SameValue(x, y),而ecma-262规范中对SameValue(x, y)规范是:

SameValue(value1, value2)
The internal comparison abstract operation SameValue(x, y), where x and y are ECMAScript language values, produces true or false. Such a comparison is performed as follows:

  • ReturnIfAbrupt(x).

  • ReturnIfAbrupt(y).

  • If Type(x) is different from Type(y), return false.

  • If Type(x) is Undefined, return true.

  • If Type(x) is Null, return true.

  • If Type(x) is Number, then

    • If x is NaN and y is NaN, return true.

    • If x is +0 and y is −0, return false.

    • If x is −0 and y is +0, return false.

    • If x is the same Number value as y, return true.

    • Return false.

  • If Type(x) is String, then

    • If x and y are exactly the same sequence of code units (same length and same code units > at corresponding indices) return true; otherwise, return false.

  • If Type(x) is Boolean, then

    • If x and y are both true or both false, return true; otherwise, return false.

  • If Type(x) is Symbol, then

    • If x and y are both the same Symbol value, return true; otherwise, return false.

  • Return true if x and y are the same Object value. Otherwise, return false.

NOTE :This algorithm differs from the Strict Equality Comparison Algorithm (7.2.13) in its treatment of signed zeroes and NaNs.

上面提到这个算法和严格比较算法(===)有所不同,我们可以标出几个不同的地方,然后在严格比较算法(===)的基础上实现SameValue(value1, value2),同时也可以实现Object.is ( value1, value2 )。

Strict Equality Comparison

The comparison x === y, where x and y are values, produces true or false. Such a comparison > is performed as follows:

  • If Type(x) is different from Type(y), return false.

  • If Type(x) is Undefined, return true.

  • If Type(x) is Null, return true.

  • If Type(x) is Number, then

    • If x is NaN, return false.

    • If y is NaN, return false.

    • If x is the same Number value as y, return true.

    • If x is +0 and y is −0, return true.

    • If x is −0 and y is +0, return true.

    • Return false.

  • If Type(x) is String, then

    • If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true.

    • Else, return false.
      If Type(x) is Boolean, then

    • If x and y are both true or both false, return true.

    • Else, return false.

  • If x and y are the same Symbol value, return true.

  • If x and y are the same Object value, return true.

  • Return false.

比较SameValue(value1, value2) 和 Strict Equality Comparison,Strict Equality Comparison不能区分两个不同的数字 -0 和 +0,还会把两个 NaN 看成是不相等的。

Polyfill(基于===)
  Object.is = function(x, y) {
    // SameValue algorithm
    if (x === y) { // Steps 1-5, 7-10
      // Steps 6.b-6.e: +0 != -0
      return x !== 0 || 1 / x === 1 / y;
    } else {
      // Step 6.a: NaN == NaN
      return x !== x && y !== y;
    }
  };

Object.assign ( target, ...sources )

Object.assign()[ES6] 方法可以把任意多个的源对象所拥有的自身可枚举属性拷贝给目标对象,然后返回目标对象。

Object.assign ( target, ...sources )
The assign function is used to copy the values of all of the enumerable own properties from > one or more source objects to a target object. When the assign function is called, the >
following steps are taken:

  • Let to be ToObject(target).

  • ReturnIfAbrupt(to).

  • If only one argument was passed, return to.

  • Let sources be the List of argument values starting with the second argument.

  • For each element nextSource of sources, in ascending index order,

    • If nextSource is undefined or null, let keys be an empty List.

    • Else,

      • Let from be ToObject(nextSource).

      • Let keys be from.[[OwnPropertyKeys]]().

      • ReturnIfAbrupt(keys).

  • Repeat for each element nextKey of keys in List order,

    • Let desc be from.[GetOwnProperty].

    • ReturnIfAbrupt(desc).

    • if desc is not undefined and desc.[[Enumerable]] is true, then

      • Let propValue be Get(from, nextKey).

      • ReturnIfAbrupt(propValue).

      • Let status be Set(to, nextKey, propValue, true).

      • ReturnIfAbrupt(status).

  • Return to.

The length property of the assign method is 2.

Ployfill

由于 ES5 里压根就没有 symbol 这种数据类型,所以这个 polyfill 也没必要去支持 symbol 属性(意思就是说,有 symbol 的环境一定有原生的 Object.assign):

if (!Object.assign) {
  Object.defineProperty(Object, "assign", {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target, firstSource) {
      "use strict";
      if (target === undefined || target === null)
        throw new TypeError("Cannot convert first argument to object");
      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) continue;
        var keysArray = Object.keys(Object(nextSource));
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) to[nextKey] = nextSource[nextKey];
        }
      }
      return to;
    }
  });
}
Demo
  • 拷贝 symbol 类型的属性

var o1 = { a: 1 };
var o2 = { [Symbol("foo")]: 2 };

var obj = Object.assign({}, o1, o2);
console.log(obj); // Object {a: 1, Symbol(foo): 2}
  • 继承属性和不可枚举属性是不能拷贝的

var obj = Object.create({foo: 1}, { // foo 是个继承属性。
    bar: {
        value: 2  // bar 是个不可枚举属性。
    },
    baz: {
        value: 3,
        enumerable: true  
    }
});
var copy = Object.assign({}, obj);
console.log(copy); //  Object {baz: 3}
  • 原始值会被隐式转换成其包装对象

var v1 = "123";
var v2 = true;
var v3 = 10;
var v4 = Symbol("foo")

var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
// 源对象如果是原始值,会被自动转换成它们的包装对象,
// 而 null 和 undefined 这两种原始值会被完全忽略。
// 注意,只有字符串的包装对象才有可能有自身可枚举属性。
console.log(obj); // { "0": "1", "1": "2", "2": "3" }
  • 拷贝属性过程中发生异常

var target = Object.defineProperty({}, "foo", {
    value: 1,
    writeable: false
}); // target 的 foo 属性是个只读属性。

Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。

console.log(target.bar);  // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo);  // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz);  // undefined,第三个源对象更是不会被拷贝到的。

Object.keys(obj)

Object.keys() 方法会返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致(两者的主要区别是 for-in 还会遍历出一个对象从其原型链上继承到的可枚举属性)。

Object.defineProperty(obj, prop, descriptor)

Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。

Object.defineProperties(obj, props)

Object.defineProperties() 方法在一个对象上添加或修改一个或者多个自有属性,并返回该对象。

Object.freeze(obj)

Object.freeze() 方法可以冻结一个对象。也就是说,这个对象永远是不可变的.

Object.isFrozen(obj)

Object.isFrozen() 方法判断一个对象是否被冻结(frozen)。ES5中规范和ES6有些不同:

If Type(O) is not Object throw a TypeError exception.(ES5)

If Type(O) is not Object, return true.(ES6)

所以Object.isFrozen(1);在ES5中会报错,而在ES6中会返回true.类似的还有Object.isExtensible()和Object.isSealed().

Object.preventExtensions(obj)

Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性

Object.isExtensible(obj)

Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)

Object.seal(obj)

bject.seal() 方法可以让一个对象密封,并返回被密封后的对象。密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。

Object.isSealed(obj)

Object.isSealed() 方法判断一个对象是否是密封的(sealed)。(密封对象是指那些不可 扩展 的,且所有自身属性都不可配置的(non-configurable)对象。)

Object.getOwnPropertyDescriptor(obj, prop)

Object.getOwnPropertyDescriptor() 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。

Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols()[ES6] 方法会返回一个数组,该数组包含了指定对象自身的(非继承的)所有 symbol 属性键。

Object.getPrototypeOf(obj)

Object.getPrototypeOf()方法返回指定对象的原型。(也就是该对象内部属性[[prototype]]的值)

Object.observe(obj, callback)

Object.observe()[ES7] 方法用于异步的监视一个对象的修改。当对象属性被修改时,方法的回调函数会提供一个有序的修改流。Object.observe().该方法在两个月之前从提议中去掉了,可以使用第三方库代替。

Object.unobserve(obj, callback)

Object.unobserve()[ES7] 是用来移除通过 Object.observe()设置的观察者的方法。

Object.getNotifer(obj)

Object.getNotifer()[ES7]可以创造一个异步触发的对象。

Object.prototype.toString()

toString() 方法返回一个代表该对象的字符串。调用该对象的toString()方法时会返回"[object type]",这里的字符串type表示了一个对象类型。所以经常使用该方法去做类型判断,因为使用typeof,无法区分array, null, function, object等。因为该方法会被子类方法覆盖,所以需要调用Object.prototype的toString()。

var toString = Object.prototype.toString;

toString.call(new Function) // [object Function]
toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]

//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]

Object.prototype.toLocaleString();

toLocaleString() 方法返回一个该对象的字符串表示。该方法主要用于被本地化相关对象覆盖。

Object.prototype.hasOwnProperty()

hasOwnProperty() 方法用来判断某个对象是否含有指定的自身属性

Object.prototype.isPrototypeOf(obj)

isPrototypeOf() 方法测试一个对象是否存在于另一个对象的原型链上。

Object.prototype.propertyIsEnumerable(obj)

propertyIsEnumerable() 方法返回一个布尔值,表明指定的属性名是否是当前对象可枚举自身属性.

Object.prototype.valueOf(obj)

Object.prototype.valueOf()方法返回特定对象的初始值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值