JavaScript中你所不知道的Object(一)
Object实在是JavaScript中很基础的东西了,在工作中,它只有那么贫瘠的几个用法,让人感觉不过尔尔,但是我们真的了解它吗?
1. 当我们习惯用
var a = {
name: 'tarol',
age: 18
};
console.log(a.age); //
a.age = 19;
console.log(a.age); //
初始化和访问对象的时候,谁会在意这种方式也是合法的:
var a = {
name: 'tarol',
_age: 18,
set age(value) {
this._age = value;
},
get age() {
return this._age;
}
};
console.log(a.age); //
a.age = 19;
console.log(a.age); //
2. 当我们习惯用
function A() {
this.name = 'tarol';
}
var a = new A();
function B() {
this.age = 18;
}
B.prototype = a;
var b = new B();
console.log(b.name); //tarol
实现继承的时候,谁会在意其实也可以这样:
var a = {
name: 'tarol'
};
var b = Object.create(a);
b.age = 18;
console.log(b.name); //tarol
3. 当我们知道原型链以后,想恶作剧修改内置函数的原型,却发现没有办法
var a = {};
Object.prototype = a;
console.log(Object.prototype === a); //false
如果你感兴趣,那么我从头说起:
首先,JavaScript中的对象是什么?ES5中只给出一句话,对象是属性的集合。它只是一个盒子,它能做什么,取决于盒子里有什么。
那么,属性是什么,一般看来,属性是一个key, value对,这个说法是对的吗?我们来剖析下属性。
从一个程序员的角度来说,属性分为可通过JS调用的的和不可通过JS调用的。不可调用的叫做内部属性,那么可调用的我们对应着叫外部属性吧。内部属性是JS解释器实现各种接口的时候使用的算法中需要调用的属性,举个栗子,有个内部属性叫[[Put]],这是一个内部方法,传入属性名和值,它的作用就是为属性赋值。所以当我们使用a.age = 18的时候,实际就调用到了这个内部属性。而外部属性又分为两种,一种是数据属性,一种是访问器属性。上面的例一中,第二种方式给对象a添加了三个属性,其中name、_age是数据属性,age是访问器属性。当属性是数据属性的时候,属性是key、value对的说法好像是对的,但当属性是访问器属性的时候,这个说法好像有问题了,因为一个key对应的是一个setter和一个getter。所以,这个说法是错的?
其实,属性不是我们看到的那样,单单就一个key对应一个数据或者一个setter加一个getter。属性还存在其他一些状态,我们称之为特性,无论是数据属性还是访问器属性,都存在四个特性。数据属性的特性为:[[Value]]、[[Writable]]、[[Enumerable]]、[[Configuration]],访问器属性的特性为:[[Get]]、[[Set]]、[[Enumerable]]、[[Configuration]]。其中[[Value]]、[[Get]]、[[Set]]相信已经很好理解了,[[Writable]]描述数据属性是否可被重新赋值,[[Enumerable]]描述属性是否可被for-in遍历,[[Configuration]]描述属性特性是否可被修改(一旦设置为false则不可以再修改此特性)。
JS开放了三个接口用于设置和获取属性的特性,分别是Object.defineProperty、Object.defineProperties和Object.getOwnPropertyDescriptor。
var a = {
name : 'tarol',
age : 18,
job : 'coder'
};
Object.defineProperty(a, 'name', {
value: 'ctarol',
writable: true,
enumerable: true,
configuration: true
});
Object.defineProperties(a, {
age: {
value: 19,
writable: true,
enumerable: true,
configuration: true
},
job: {
value: 'mental',
writable: true,
enumerable: true,
configuration: true
}
});
console.log(a.name); //tarol
console.log(a.age); //
console.log(Object.getOwnPropertyDescriptor(a, 'job')); //Object {value: "mental", writable: true, enumerable: true, configurable: true}
总的看来,属性还是可以作为一个key, value对的,但这个value不是我们赋的值,而是整个属性特性的集合,我们称之为属性描述。
外部属性的问题解决了,内部属性我们还只是蜻蜓点水般浅尝辄止,所以接下来我们开始从内部属性入手,对JS中的对象做一个更深刻的认识。以下是内部属性的表格:
| 属性名 | 用途 | 属性类型 |
方法返回值 (仅适用方法) |
他处引用 (仅适用数据) |
他处赋值 (仅适用数据) |
他处调用 (仅适用方法) |
调用其他 (仅适用方法) |
| [[Prototype]] | 对象原型 | Object |
__proto__ etc. |
||||
| [[Class]] | 对象类型 | String | Object.prototype.toString() | ||||
| [[Extensible]] | 可否添加属性 | Boolean |
Object.seal(obj) --> false Object.freeze(obj) --> false Object.preventExtensions(obj) --> false |
||||
| [[GetOwnProperty]] | 返回自身指定的属性描述 | func('prop') | 属性描述 |
Object.getOwnPropertyDescriptor(obj, 'prop') [[GetProperty]] |
|||
| [[GetProperty]] | 返回原型链上指定的属性描述 | func('prop') | 属性描述 | [[GetOwnProperty]] | |||
| [[HasProperty]] | 返回原型链上是否有指定属性 | func('prop') | Boolean | [[GetProperty]] | |||
| [[DefineOwnProperty]] | 创建或修改自身的属性描述 | func('prop', desc, Boolean) | Boolean |
Object.defineProperty(obj, 'prop', desc) Object.defineProperties(obj, descs) |
|||
| [[DefaultValue]] | 将对象转换为对应的基础类型 | func(String/Number) | String / Number |
toString() valueOf() |
|||
| [[Delete]] | 删除对象的属性 | func('prop', Boolean) | Boolean | [[GetOwnProperty]] | |||
| [[CanPut]] | 可否设置属性的值 | func('prop') | Boolean |
[[GetOwnProperty]] [[GetProperty]] [[Extensible]] |
|||
| [[Get]] | 获取属性的值 | func('prop') | mixin | [[GetProperty]] | |||
| [[Put]] | 设置属性的值 | func('prop', mixin, Boolean) | Boolean |
[[CanPut]] [[GetOwnProperty]] [[GetProperty]] [[DefineOwnProperty]] |
上面的表格稍显晦涩,看不懂不要紧,我们来分下类。内部属性中除了[[Class]]、[[DefaultValue]]用于展示信息以外,其他都是用来操作外部属性的,可见对象的核心就是属性。其中我列出[[CanPut]]和[[Put]]的算法实现,因为这两个方法的实现涵盖了基本所有的属性操作和思想。
[[CanPut]]:

[[Put]]:

前面提到过,我们使用a.age=18进行赋值的时候,调用的就是[[Put]]这个内部方法。由上图算法可知,当对属性赋值时,只要这个属性不是原型链上的访问器属性,那么就会修改或产生自身的数据属性,即不存在一种情况,就是修改原型链上的数据属性。我们测试下:
var a = {
name: 'tarol',
_age: 18,
set age(value) {
this._age = value;
},
get age() {
return this._age;
}
};
var b = Object.create(a);
console.log(b.hasOwnProperty('name')); //false
console.log(b.hasOwnProperty('_age')); //false
console.log(b.hasOwnProperty('age')); //false
b.name = 'okal';
b.age = 19;
console.log(b.hasOwnProperty('name')); //true
console.log(b.hasOwnProperty('_age')); //true
console.log(b.hasOwnProperty('age')); //false
console.log(a.name); //tarol
console.log(a.age); //
由结果可知,我们在对name这个原型链上的数据属性进行赋值时,实际上是重新创建了一个自身属性,对原型上的数据属性是没有影响的。而调用访问器属性age的[[Set]]方法的时候,传入的this也是当前的对象而不是访问器属性的拥有者,所以在当前对象上创建了一个自身属性_age。
好了,上面说的是通用的内部属性,即Object类型的内部属性,而像Boolean、Date、Number、String、Function等拥有更多的内部属性,就留到下一篇再说。
JavaScript中你所不知道的Object(一)的更多相关文章
- JavaScript中你所不知道的Object(二)--Function篇
上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.Str ...
- Visual Studio中你所不知道的智能感知
在Visual Studio中的智能感知,相信大家都用过.summary,param,returns这几个相信很多人都用过的吧.那么field,value等等这些呢. 首先在Visual Studio ...
- KVO中你所不知道的"坑"
一.什么是 KVO 首先让我们了解一下什么KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.键值观察Key-Value ...
- Go基础之--位操作中你所不知道的用法
之前一直忽略的就是所有语言中关于位操作,觉得用处并不多,可能用到也非常简单的用法,但是其实一直忽略的是它们的用处还是非常大的,下面先回顾一下位操作符的基础 位操作符 与操作:&1 & ...
- 前端开发 CSS中你所不知道的伪类与伪元素的区别--摘抄
做过前端开发的人都熟悉伪类与伪元素,而真正能够彻底了解这二者的区别的人并不多.伪类与伪元素确实很容易混淆. 伪元素主要是用来创建一些不存在原有dom结构树种的元素,例如:用::before和::aft ...
- js值----你所不知道的JavaScript系列(6)
1.数组 在 JavaScript 中,数组可以容纳任何类型的值,可以是字符串.数字.对象(object),甚至是其他数组(多维数组就是通过这种方式来实现的) .----<你所不知道的JavaS ...
- js类型----你所不知道的JavaScript系列(5)
ECMAScirpt 变量有两种不同的数据类型:基本类型,引用类型.也有其他的叫法,比如原始类型和对象类型等. 1.内置类型 JavaScript 有七种内置类型: • 空值(null) • 未定义( ...
- 闭包----你所不知道的JavaScript系列(4)
一.闭包是什么? · 闭包就是可以使得函数外部的对象能够获取函数内部的信息. · 闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. · 闭包就 ...
- Android中Context详解 ---- 你所不知道的Context
转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好, ...
随机推荐
- 表达式求值(栈方法/C++语言描述)(一)
一个算数表达式(以下简称为表达式)由运算数.运算符.左括号和右括号组成,定义一个枚举类型TokenType表示为: typedef enum { BEGIN, NUMBER, OPERATOR, LE ...
- 关于flask线程安全的简单研究
flask是python web开发比较主流的框架之一,也是我在工作中使用的主要开发框架.一直对其是如何保证线程安全的问题比较好奇,所以简单的探究了一番,由于只是简单查看了源码,并未深入细致研究,因此 ...
- 二分图最大匹配:匈牙利算法的python实现
二分图匹配是很常见的算法问题,一般用匈牙利算法解决二分图最大匹配问题,但是目前网上绝大多数都是C/C++实现版本,没有python版本,于是就用python实现了一下深度优先的匈牙利算法,本文使用的是 ...
- Java面试题大全
前言 关于赢在面试的Java题系列基本收集整理完成了,所有题目都是经过精心挑选的,很基础又考验求职者的基本功,应该说被面试到的几率很大.这里整理挑选出来供大家面试前拿来看一看,所有题目整理自网络,有一 ...
- Python 继承和组合 接口
#解决代码重用的问题,减少代码冗余 #继承是一种什么'是'什么的关系 class People: def __init__(self, name, age): # print('People.__in ...
- 解析spring循环依赖策略
循环依赖 所谓循环依赖就是多个Bean之间依赖关系形成一个闭环,例如A->B->C->...->A 这种情况,当然,最简单的循环依赖就是2个Bean之间互相依赖:A->B ...
- WebService核心文件【web-config.wsdd】调用操作
WebService核心文件[server-config.wsdd]详解及调用示例 作者:玛莎拉蒂-小贱人 一.准备工作 导入需要的jar包: 二.配置web.xml 在web工程的web.xml中添 ...
- 第9天:CSS精灵图
今天重点学习了CSS精灵图. "CSS精灵",英语css sprite,所以也叫做"CSS雪碧"技术.是一种CSS图像合并技术,该方法是将小图标和背景图像合并到 ...
- ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十二节--小结,Bootstrap Table之角色管理
返回总目录:ABP+AdminLTE+Bootstrap Table权限管理系统一期 很多人说ABP不适合高并发大型,有一定的道理,但是我觉得还是可以的,就看架构师的能力了,哈哈,我之前公司就是ABP ...
- 修改(同步)linux时间
新申请下来的机器的时间现在我们所在的时区不一致,需要同步成东八区 查看当前的时间: date -R; date +%z 修改时区: cp /usr/share/zoneinfo/Asia/Shangh ...