资讯专栏INFORMATION COLUMN

重读Javascript之Object

Alex / 807人阅读

摘要:对象是中最常的内置对象之一。为了节省内存,使用一个共享的构造器使用更安全的引用如果不是或,抛出一个异常。使创建的一个新的对象为,就和通过表达式创建一个新对象一样,是标准内置的构造器名设置的内部属性为。方法返回一个该对象的字符串表示。

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()方法返回特定对象的初始值。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/78387.html

相关文章

  • 重读javascriptFunction

    摘要:函数用于指定对象的行为。命名函数的赋值表达式另外一个特殊的情况是将命名函数赋值给一个变量。这是由于的命名处理所致,函数名在函数内总是可见的。该方法返回一个表示当前函数源代码的字符串。 函数包含一组语句,它们是JavaScript的基础模块单元,用于代码的复用、信息隐藏和组合调用。函数用于指定对象的行为。一般来说,所谓编程,就是将一组需求分解成函数与数据结构的技能。 JavaScript...

    txgcwm 评论0 收藏0
  • 重读 JavaScript DOM 编程艺术(一)--DOM 的增删改查

    摘要:在很久之前读过编程艺术,现在重读又有新的体会,遂记录下。唯一没有被其他元素包含的元素是元素,它是的根元素。是节点内的第一个子节点,所以将是一个值,应该写成才能得到。操作操作无非是增删改查,我们先看查和改。 在很久之前读过JavaScript Dom 编程艺术,现在重读又有新的体会,遂记录下。 什么是DOM 对于这种英文缩写,首先看它的英文全拼--Document Object Mode...

    songze 评论0 收藏0
  • 重读《学习JavaScript数据结构与算法-第三版》- 第5章 队列

    摘要:定场诗马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁前言本章为重读学习数据结构与算法第三版的系列文章,主要讲述队列数据结构双端队列数据结构以及队列相关应用。 定场诗 马瘦毛长蹄子肥,儿子偷爹不算贼,瞎大爷娶个瞎大奶奶,老两口过了多半辈,谁也没看见谁! 前言 本章为重读《学习JavaScript数据结构与算法-第三版》的系列文章,主要讲述队列数据结构、...

    charles_paul 评论0 收藏0
  • 重读《学习JavaScript数据结构与算法-第三版》-第2章 ECMAScript与TypeScr

    摘要:第二种接口的概念和面向对象编程相关接口视为一份合约,在合约里可以定义这份合约的类或接口的行为接口告诉类,它需要实现一个叫做的方法,并且该方法接收一个参数。 定场诗 八月中秋白露,路上行人凄凉; 小桥流水桂花香,日夜千思万想。 心中不得宁静,清早览罢文章, 十年寒苦在书房,方显才高志广。 前言 洛伊安妮·格罗纳女士所著的《学习JavaScript数据结构与算法》第三版于2019年的5月份...

    TZLLOG 评论0 收藏0
  • 重读你不知道的JS (上) 第一节三章

    摘要:如果是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。给函数表达式指定一个函数名可以有效的解决以上问题。始终给函数表达式命名是一个最佳实践。也有开发者干脆关闭了静态检查工具对重复变量名的检查。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 Ja...

    lavor 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<