JavaScript 对象所有API解析【2024版】

var person = {};

Object.defineProperty(person, ‘legs’, {

set:function(v) {

return this.value = v;

},

get: function(v) {

return this.value;

},

configurable: true,

enumerable: true

});

person.legs = 2;

这样一来,多了许多可以用来描述属性的代码,如果想要防止别人篡改我们的属性,就必须要用到它们。此外,也不要忘了浏览器向后兼容ES3方面所做的考虑。例如,跟添加Array.prototype属性不一样,我们不能在旧版的浏览器中使用shim这一特性。另外,我们还可以(通过定义nonmalleable属性),在具体行为中运用这些描述符:

var person = {};

Object.defineProperty(person, ‘heads’, {value: 1});

person.heads = 0; // 0

person.heads; // 1 (改不了)

delete person.heads; // false

person.heads // 1 (删不掉)

Object.defineProperty(obj, prop, descriptor) (ES5)

具体用法可参见上文,或者查看 MDN。MDN Object.defineProperty(obj, descriptor)[4]

Vue.js 文档:**如何追踪变化**[5] 把一个普通 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。Object.defineProperty 是仅 ES5 支持,且无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因。

Object.defineProperties(obj, props) (ES5)

该方法的作用与defineProperty()基本相同,只不过它可以用来一次定义多个属性。比如:

var glass = Object.defineProperties({}, {

‘color’: {

value: ‘transparent’,

writable: true

},

‘fullness’: {

value: ‘half’,

writable: false

}

});

glass.fullness; // ‘half’

Object.getPrototypeOf(obj) (ES5)

之前在ES3中,我们往往需要通过Object.prototype.isPrototypeOf()去猜测某个给定的对象的原型是什么,如今在ES5中,我们可以直接询问改对象“你的原型是什么?”

Object.getPrototypeOf([]) === Array.prototype; // true

Object.getPrototypeOf(Array.prototype) === Object.prototype; // true

Object.getPrototypeOf(Object.prototype) === null; // true

Object.create(obj, descr) (ES5)

该方法主要用于创建一个新对象,并为其设置原型,用(上述)属性描述符来定义对象的原型属性。

var parent = {hi: ‘Hello’};

var o = Object.create(parent, {

prop: {

value: 1

}

});

o.hi; // ‘Hello’

// 获得它的原型

Object.getPrototypeOf(parent) === Object.prototype; // true 说明parent的原型是Object.prototype

Object.getPrototypeOf(o); // {hi: “Hello”} // 说明o的原型是{hi: “Hello”}

o.hasOwnProperty(‘hi’); // false 说明hi是原型上的

o.hasOwnProperty(‘prop’); // true 说明prop是原型上的自身上的属性。

现在,我们甚至可以用它来创建一个完全空白的对象,这样的事情在ES3中可是做不到的。

var o = Object.create(null);

typeof o.toString(); // ‘undefined’

Object.getOwnPropertyDesciptor(obj, property) (ES5)

该方法可以让我们详细查看一个属性的定义。甚至可以通过它一窥那些内置的,之前不可见的隐藏属性。

Object.getOwnPropertyDescriptor(Object.prototype, ‘toString’);

// {writable: true, enumerable: false, configurable: true, value: ƒ toString()}

Object.getOwnPropertyNames(obj) (ES5)

该方法返回一个数组,其中包含了当前对象所有属性的名称(字符串),不论它们是否可枚举。当然,也可以用Object.keys()来单独返回可枚举的属性。

Object.getOwnPropertyNames(Object.prototype);

// [“defineGetter”, “defineSetter”, “hasOwnProperty”, “lookupGetter”, “lookupSetter”, “propertyIsEnumerable”, “toString”, “valueOf”, “proto”, “constructor”, “toLocaleString”, “isPrototypeOf”]

Object.keys(Object.prototype);

// []

Object.getOwnPropertyNames(Object);

// [“length”, “name”, “arguments”, “caller”, “prototype”, “assign”, “getOwnPropertyDescriptor”, “getOwnPropertyDescriptors”, “getOwnPropertyNames”, “getOwnPropertySymbols”, “is”, “preventExtensions”, “seal”, “create”, “defineProperties”, “defineProperty”, “freeze”, “getPrototypeOf”, “setPrototypeOf”, “isExtensible”, “isFrozen”, “isSealed”, “keys”, “entries”, “values”]

Object.keys(Object);

// []

Object.preventExtensions(obj) (ES5)

Object.isExtensible(obj) (ES5)

preventExtensions()方法用于禁止向某一对象添加更多属性,而isExtensible()方法则用于检查某对象是否还可以被添加属性。

var deadline = {};

Object.isExtensible(deadline); // true

deadline.date = ‘yesterday’; // ‘yesterday’

Object.preventExtensions(deadline);

Object.isExtensible(deadline); // false

deadline.date = ‘today’;

deadline.date; // ‘today’

// 尽管向某个不可扩展的对象中添加属性不算是一个错误操作,但它没有任何作用。

deadline.report = true;

deadline.report; // undefined

Object.seal(obj) (ES5)

Object.isSeal(obj) (ES5)

seal()方法可以让一个对象密封,并返回被密封后的对象。seal()方法的作用与preventExtensions()基本相同,但除此之外,它还会将现有属性 设置成不可配置。也就是说,在这种情况下,我们只能变更现有属性的值,但不能删除或(用defineProperty())重新配置这些属性,例如不能将一个可枚举的属性改成不可枚举。

var person = {legs:2};

// person === Object.seal(person); // true

Object.isSealed(person); // true

Object.getOwnPropertyDescriptor(person, ‘legs’);

// {value: 2, writable: true, enumerable: true, configurable: false}

delete person.legs; // false (不可删除,不可配置)

Object.defineProperty(person, ‘legs’,{value:2});

person.legs; // 2

person.legs = 1;

person.legs; // 1 (可写)

Object.defineProperty(person, “legs”, { get: function() { return “legs”; } });

// 抛出TypeError异常

Object.freeze(obj) (ES5)

Object.isFrozen(obj) (ES5)

freeze()方法用于执行一切不受seal()方法限制的属性值变更。Object.freeze() 方法可以冻结一个对象,冻结指的是不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。

var deadline = Object.freeze({date: ‘yesterday’});

deadline.date = ‘tomorrow’;

deadline.excuse = ‘lame’;

deadline.date; // ‘yesterday’

deadline.excuse; // undefined

Object.isSealed(deadline); // true;

Object.isFrozen(deadline); // true

Object.getOwnPropertyDescriptor(deadline, ‘date’);

// {value: “yesterday”, writable: false, enumerable: true, configurable: false} (不可配置,不可写)

Object.keys(deadline); // [‘date’] (可枚举)

Object.keys(obj) (ES5)

该方法是一种特殊的for-in循环。它只返回当前对象的属性(不像for-in),而且这些属性也必须是可枚举的(这点和Object.getOwnPropertyNames()不同,不论是否可以枚举)。返回值是一个字符串数组。

Object.prototype.customProto = 101;

Object.getOwnPropertyNames(Object.prototype);

// […, “constructor”, “toLocaleString”, “isPrototypeOf”, “customProto”]

Object.keys(Object.prototype); // [‘customProto’]

var o = {own: 202};

o.customProto; // 101

Object.keys(o); // [‘own’]

四、在ES6中附加的Object属性


Object.is(value1, value2) (ES6)

该方法用来比较两个值是否严格相等。它与严格比较运算符(===)的行为基本一致。不同之处只有两个:一是+0不等于-0,而是NaN等于自身。

Object.is(‘若川’, ‘若川’); // true

Object.is({},{}); // false

Object.is(+0, -0); // false

+0 === -0; // true

Object.is(NaN, NaN); // true

NaN === NaN; // false

ES5可以通过以下代码部署Object.is

Object.defineProperty(Object, ‘is’, {

value: function() {x, y} {

if (x === y) {

// 针对+0不等于-0的情况

return x !== 0 || 1 / x === 1 / y;

}

// 针对 NaN的情况

return x !== x && y !== y;

},

configurable: true,

enumerable: false,

writable: true

});

Object.assign(target, …sources) (ES6)

该方法用来源对象(source)的所有可枚举的属性复制到目标对象(target)。它至少需要两个对象作为参数,第一个参数是目标对象target,后面的参数都是源对象(source)。只有一个参数不是对象,就会抛出TypeError错误。

var target = {a: 1};

var source1 = {b: 2};

var source2 = {c: 3};

obj = Object.assign(target, source1, source2);

target; // {a:1,b:2,c:3}

obj; // {a:1,b:2,c:3}

target === obj; // true

// 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

var source3 = {a:2,b:3,c:4};

Object.assign(target, source3);

target; // {a:2,b:3,c:4}

Object.assign只复制自身属性,不可枚举的属性(enumerablefalse)和继承的属性不会被复制。

Object.assign({b: ‘c’},

Object.defineProperty({}, ‘invisible’, {

enumerable: false,

value: ‘hello’

})

);

// {b: ‘c’}

属性名为Symbol值的属性,也会被Object.assign()复制。

Object.assign({a: ‘b’}, {[Symbol(‘c’)]: ‘d’});

// {a: ‘b’, Symbol©: ‘d’}

对于嵌套的对象,Object.assign()的处理方法是替换,而不是添加。

Object.assign({a: {b:‘c’,d:‘e’}}, {a:{b:‘hello’}});

// {a: {b:‘hello’}}

对于数组,Object.assign()把数组视为属性名为 0、1、2 的对象。

Object.assign([1,2,3], [4,5]);

// [4,5,3]

Object.getOwnPropertySymbols(obj) (ES6)

该方法会返回一个数组,该数组包含了指定对象自身的(非继承的)所有 symbol 属性键。该方法和 Object.getOwnPropertyNames() 类似,但后者返回的结果只会包含字符串类型的属性键,也就是传统的属性名。

Object.getOwnPropertySymbols({a: ‘b’, [Symbol(‘c’)]: ‘d’});

// [Symbol©]

Object.setPrototypeOf(obj, prototype) (ES6)

该方法设置一个指定的对象的原型 ( 即, 内部[[Prototype]]属性)到另一个对象或 null__proto__属性用来读取或设置当前对象的prototype对象。目前,所有浏览器(包括IE11)都部署了这个属性。

// ES6写法

var obj = {

method: function(){

// code …

}

};

// obj.proto = someOtherObj;

// ES5写法

var obj = Object.create(someOtherObj);

obj.method = function(){

// code …

};

该属性没有写入ES6的正文,而是写入了附录。__proto__前后的双下划线说明它本质上是一个内部属性,而不是正式对外的一个 API。无论从语义的角度,还是从兼容性的角度,都不要使用这个属性。而是使用Object.setPrototypeOf()(写操作),Object.getPrototypeOf()(读操作),或Object.create()(生成操作)代替。在实现上,__proto__调用的Object.prototype.__proto__Object.setPrototypeOf()方法的作用与__proto__作用相同,用于设置一个对象的prototype对象。它是ES6正式推荐的设置原型对象的方法。

五、在ES8中附加的Object属性


Object.getOwnPropertyDescriptors(obj) (ES8)

该方法基本与Object.getOwnPropertyDescriptor(obj, property)用法一致,只不过它可以用来获取一个对象的所有自身属性的描述符。

Object.getOwnPropertyDescriptor(Object.prototype, ‘toString’);

// {writable: true, enumerable: false, configurable: true, value: ƒ toString()}

Object.getOwnPropertyDescriptors(Object.prototype); // 可以自行在浏览器控制台查看效果。

Object.values(obj) (ES8)

Object.values() 方法与Object.keys类似。返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for...in循环的顺序相同 ( 区别在于for-in循环枚举原型链中的属性 )。

var obj = {a:1,b:2,c:3};

Object.keys(obj); // [‘a’,‘b’,‘c’]

Object.values(obj); // [1,2,3]

Object.entries(obj) (ES8)

Object.entries() 方法返回一个给定对象自己的可枚举属性[key,value]对的数组,数组中键值对的排列顺序和使用 for...in 循环遍历该对象时返回的顺序一致(区别在于一个for-in循环也枚举原型链中的属性)。

var obj = {a:1,b:2,c:3};

Object.keys(obj); // [‘a’,‘b’,‘c’]

Object.values(obj); // [1,2,3]

Object.entries(obj); // [[‘a’,1],[‘b’,2],[‘c’,3]]

六、在ES10中附加的Object属性


Object.fromEntries(iterable) (ES10)

Object.fromEntries()方法返回一个给定可迭代对象(类似ArrayMap或其他可迭代对象)对应属性的新对象。

Object.fromEntries() 是 Object.entries()的逆操作。

var arr = [[‘a’,1],[‘b’,2],[‘c’,3]];

Object.fromEntries(obj); // {a: 1, b: 2, c: 3}

var entries = new Map([

[‘name’, ‘若川’],

[‘age’, 18]

]);

Object.fromEntries(entries) // {name: ‘若川’, age: 18}

小结

细心的读者可能会发现MDN上还有一些API,本文没有列举到。因为那些是非标准的API。熟悉对象的 API 对理解原型和原型链相关知识会有一定帮助。常用的 API 主要有Object.prototype.toString()Object.prototype.hasOwnProperty(), Object.getPrototypeOf(obj)Object.create()Object.definePropertyObject.keys(obj)Object.assign()

如果读者发现有不妥或可改善之处,再或者哪里没写明白的地方,欢迎评论指出。另外觉得写得不错,对您有些许帮助,可以点赞、评论、转发分享,也是对笔者的一种支持,非常感谢呀。

参考资料


MDN Object API[6]

JavaScript 面向对象编程指南(第 2 版)(豆瓣读书链接)[7]

阮一峰 ES6 标准入门 2[8]

原创精选文章


工作一年后,我有些感悟(写于2017年)

高考七年后、工作三年后的感悟

面试官问:JS的继承

前端使用puppeteer 爬虫生成《React.js 小书》PDF并合并

学习 jQuery 源码整体架构,打造属于自己的 js 类库

学习underscore源码整体架构,打造属于自己的函数式编程类库

学习 lodash 源码整体架构,打造属于自己的函数式编程类库

学习 sentry 源码整体架构,打造属于自己的前端异常监控SDK

学习 vuex 源码整体架构,打造属于自己的状态管理库

学习 axios 源码整体架构,打造属于自己的请求库

知乎问答:一年内的前端看不懂前端框架源码怎么办?

作者简介


作者:常以若川为名混迹于江湖。前端路上 | PPT 爱好者 | 所知甚少,唯善学。

博客:https://lxchuan12.cn/posts/,阅读体验可能更好些。

若川视野

主要发布前端 | PPT | 生活 | 效率相关的文章,长按扫码关注。欢迎加我微信lxchuan12(注明来源,基本来者不拒),拉您进【前端视野交流群】,长期交流学习~

参考资料

[1]

vue-router 源码里就是类似这样写的: https://github.com/vuejs/vue-router/blob/dev/src/install.js#L38-L44

[2]
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

核心竞争力,怎么才能提高呢?

成年人想要改变生活,逆转状态?那就开始学习吧~

万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值