勿忘初心

JavaScript中Object原生方法总结

关于ES5和ES6 Object中经常看到的方法的总结和梳理。虽然在日常开发中不经常使用,但是理解这些方法的含义对理解js有很大帮助。仅代表个人意见,有更好看法欢迎交流探讨。有些总结并不详细,只是把文章结构写出来,后续逐步更新,实在是有时候写着写着不知道怎么写,就先把想写列出来,以后慢慢写了。

prototype

js的原型对象:js中每个数据类型都是对象(除了null和undefined),而每个对象都继承自另外一个对象,后者称为“原型”(prototype)对象,只有null除外,它没有自己的原型对象。 原型对象上的所有属性和方法,都会被对象实例所共享。 通过构造函数生成对象实例时,会将对象实例的原型指向构造函数的prototype属性。每一个构造函数都有一个prototype属性,这个属性就是对象实例的原型对象。

isPrototypeOf

isPrototypeOf()函数用于指示对象是否存在于另一个对象的原型链中。如果存在,返回true,否则返回false。 该方法属于Object对象,由于所有的对象都”继承”了Object的对象实例,因此几乎所有的实例对象都可以使用该方法。

defineProperty和defineProperties

Object的defineProperty和defineProperties这两个方法在js中的重要性十分重要,主要功能就是用来定义或修改这些内部属性,与之相对应的getOwnPropertyDescriptor和getOwnPropertyDescriptors就是获取这行内部属性的描述。

  • 数据(数据描述符)属性 数据属性有4个描述内部属性的特性 [[Configurable]] 表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true [[Enumerable]] 表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性,如果直接使用字面量定义对象,默认值为true [[Writable]] 能否修改属性的值,如果直接使用字面量定义对象,默认值为true [[Value]] 该属性对应的值,默认为undefined

  • 访问器(存取描述符)属性 访问器属性也有4个描述内部属性的特性 [[Configurable]] 和数据属性的[[Configurable]]一样,表示能否通过delete删除此属性,能否修改属性的特性,或能否修改把属性修改为访问器属性,如果直接使用字面量定义对象,默认值为true [[Enumerable]] 和数据属性的[[Configurable]]一样,表示该属性是否可枚举,即是否通过for-in循环或Object.keys()返回属性,如果直接使用字面量定义对象,默认值为true [[Get]] 一个给属性提供 getter 的方法(访问对象属性时调用的函数,返回值就是当前属性的值),如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined [[Set]] 一个给属性提供 setter 的方法(给对象属性设置值时调用的函数),如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined

  • Object.defineProperty() 功能: 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。如果不指定configurable, writable, enumerable ,则这些属性默认值为false,如果不指定value, get, set,则这些属性默认值为undefined。 语法: Object.defineProperty(obj, prop, descriptor) obj: 需要被操作的目标对象 prop: 目标对象需要定义或修改的属性的名称 descriptor: 将被定义或修改的属性的描述符

    1
    2
    3
    4
    5
    6
    7
    8
    var obj = new Object();
    Object.defineProperty(obj, 'name', {
    configurable: false,
    writable: true,
    enumerable: true,
    value: '张三'
    })
    console.log(obj.name) //张三
  • Object.defineProperties() 功能: 方法直接在一个对象上定义一个或多个新的属性或修改现有属性,并返回该对象。

语法: Object.defineProperties(obj, props) obj: 将要被添加属性或修改属性的对象 props: 该对象的一个或多个键值对定义了将要为对象添加或修改的属性的具体配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = new Object();
Object.defineProperties(obj, {
name: {
value: '张三',
configurable: false,
writable: true,
enumerable: true
},
age: {
value: 18,
configurable: true
}
})
console.log(obj.name, obj.age) // 张三, 18

简单的双向数据绑定

html代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<p>
input1=><input type="text" id="input1">
</p>
<p>
input2=>
<input type="text" id="input2">
</p>
<div>
我每次比input1的值加1=>
<span id="span"></span>
</div>
</body>

js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var oInput1 = document.getElementById('input1');
var oInput2 = document.getElementById('input2');
var oSpan = document.getElementById('span');
var obj = {};
Object.defineProperties(obj, {
val1: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput2.value = newValue;
oSpan.innerHTML = Number(newValue) ? Number(newValue) : 0
}
},
val2: {
configurable: true,
get: function() {
oInput1.value = 0;
oInput2.value = 0;
oSpan.innerHTML = 0;
return 0
},
set: function(newValue) {
oInput1.value = newValue;
oSpan.innerHTML = Number(newValue)+1;
}
}
})
oInput1.value = obj.val1;
oInput1.addEventListener('keyup', function() {
obj.val1 = oInput1.value;
}, false)
oInput2.addEventListener('keyup', function() {
obj.val2 = oInput2.value;
}, false)

hasOwnProperty

判断一个属性是定义在对象本身而不是继承自原型链,我们需要使用从 Object.prototype 继承而来的 hasOwnProperty 方法。 hasOwnProperty 方法是 Javascript 中唯一一个处理对象属性而不会往上遍历原型链的。

1
2
3
4
5
6
7
8
9
// Poisoning Object.prototype
Object.prototype.bar = 1;
var foo = {goo: undefined};
foo.bar; // 1
'bar' in foo; // true
foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true:类型不等,true

getOwnPropertyNames

Object.getOwnPropertyNames()方法返回对象的所有自身属性的属性名(包括不可枚举的属性)组成的数组,但不会获取原型链上的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function A(a,aa) {
this.a = a;
this.aa = aa;
this.getA = function() {
return this.a;
}
}
// 原型方法
A.prototype.aaa = function () {};
var B = new A('b', 'bb');
B.myMethodA = function() {};
// 不可枚举方法
Object.defineProperty(B, 'myMethodB', {
enumerable: false,
value: function() {}
});
Object.getOwnPropertyNames(B); // ["a", "aa", "getA", "myMethodA", "myMethodB"]

关于ES6中Objec.keys和ES5中Object.getOwnPropertyNames

Object.keys()用于获取对象自身所有的可枚举的属性值,但不包括原型中的属性,然后返回一个由属性名组成的数组。注意它同for..in一样不能保证属性按对象原来的顺序输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 遍历数组
var colors = ['red', 'green', 'blue'];
colors.length = 10;
colors.push('yellow');
Array.prototype.demo = function () {};
Object.keys(colors); // 0 1 2 10
// 遍历对象
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.demo = function() {};
var jenemy = new Person('jenemy', 25);
Object.keys(jenemy); // name age

其实这几个方法之间的差异主要在属性是否可可枚举,是来自原型,还是实例。 |方法| |适用范围| |描述| |for..in| |数组,对象| |获取可枚举的实例和原型属性名| |Object.keys()| |数组,对象| |返回可枚举的实例属性名组成的数组| |Object.getPropertyNames()| |数组,对象| |返回除原型属性以外的所有属性(包括不可枚举的属性)名组成的数组| |for..of| |可迭代对象(Array, Map, Set, arguments等)| |返回属性值|

getOwnPropertyDescriptor

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

语法: Object.getOwnPropertyDescriptor(obj, prop) obj: 需要查找的目标对象 prop: 目标对象内属性名称

1
2
3
4
5
6
7
8
9
10
11
12
13
var person = {
name: '张三',
age: 18
}
var desc = Object.getOwnPropertyDescriptor(person, 'name');
console.log(desc) 结果如下
// {
// configurable: true,
// enumerable: true,
// writable: true,
// value: "张三"
// }

getOwnPropertySymbols

描述:ES6中新增语法,返回对象的自有符号属性。对象的自有符号属性是指直接在此对象上定义、而非从对象的原型继承的属性。

语法:Object.getOwnPropertySymbols(object);

例子:

1
2
3
4
5
var obj = {};
var key = Symbol('description');
obj[key] = 'data';
var symbols = Object.getOwnPropertySymbols(obj);
// Symbol(description)

getPrototypeOf

关于ES5中getPrototypeOf方法的理解可以与__proto__、prototype一起探讨。

  • prototype: 一般用来为一个类型建立它的原型继承对象。用于继承、实例的方法或者属性共享。
  • getPrototypeOf: Object.getPrototypeOf(obj)是ES5中用来得到obj对象的原型对象的标准方法。
  • __proto__: obj.__proto__是一个非标准的用来得到obj对象的原型对象的方法。下文提到的__proto__都是在chrom浏览器环境下的测试结果。

说到getPrototypeOf,就必须提及原型、原型链、prototype、__proto__了。下面用一张图来说明它们之间千丝万缕的关系:

  1. 在JS中,万物皆对象。除了null以外所有对象都含有__proto__属性。而子类实例的__proto__指向父类构造函数的原型对象:child.__proto__ == parents.prototype。这也保证了子类实例对象能够访问父类的原型中方法和属性。

  2. 任何Function类型对象都拥有prototype、__proto__这两个重要属性,也包含了其他的属性:arguments、caller、length、name、自定义的静态属性和方法以及方法的执行上下文。假如有一个方法test,在test方法的原型对象prototype中包含了3大块:

    1. constructor:这是一个指针,指向构造函数本身,test.prototype.constructor=test。
    2. __proto__:继承test父类的prototype属性,test.prototype.__proto__ = Object.prototype。
    3. 自定义的原型方法或者属性:这些属性都可以被test的实例继承,例如:
1
2
3
4
5
function test(){}
test.prototype.a = 1;
var b = new test();
//那么 b.\_\_proto\_\_ == test.prototype
//那么b.a => 1;这个a就是从test的prototyp上继承过来的。

备注:顺带提一下静态属性和方法:就是不能被子类继承只能通过自身来访问的属性和方法,例如:

1
2
3
4
function test(){};
test.x = 1;
test.y = function(){};
//这里x、y就只能通过test来访问,不会被子类实例继承。

关于Function和Object的联系:上面提到 child.__proto__ == parents.prototype。

  • 对象Object的构造函数也是方法类型,Object.__proto__的打印结果是一个空方法:function Empty(){};那么代表了Object.__proto__ == Function.prototype。
  • 而Function.__proto__的打印结果也是一个空方法:function Empty(){};(这里是是一个特例),这说明:Function.__proto__ == Function.prototype。
  • 所以Object和Function之间关系可以这样认为:Function.__proto__ == Object.__proto__ == Function.prototype ==>function Empty(){}。
  • 然而还没完,在js里万物皆对象,这里的Empty方法也是对象类型,说明Empty.__proto__ == Object.prototype。(这里Empty代表Function.__proto__)。貌似还没完:Empty.__proto__.__proto__ ==>null,我们发现这一切都归于了虚无(null),而在js中null也是一个特殊的对象,在初始化变量都会初始化为null,仿佛这不是结束而是新的开始。研究到这里,不禁感叹啊,真是生于此,终于此,As the end, so the start。

create

  • 关于Object.create和new Object两者的区别和实现猜想: Object.create()方法是ECMAScript5中新增的,用来规范化原型式继承的。object.create一般用来创建obj,其实质就是将源对象的原型引用指向到实例的proto上,var a = {a:1}; var b = Object.create(a); b的结构:{proto:{a:1,proto{…}}},{a:1,proto{…}}就是a。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //Object.create的实现原理
    function ObjectCreate(o){
    function F(){};
    F.prototype = o;
    return new F();
    }
    //new Object的实现原理
    function newObject(){
    var obj = new Object(); // 从Object.prototype 上克隆一个空的对象
    var Constructor = [].shift.call(arguments); // 取得外部传入的构造器,此例是Person
    obj.__proto__ = Constructor.prototype; // 指向正确的原型
    var ret = Constructor.apply(obj, arguments); // 借用外部传入的构造器给obj 设置属性
    return typeof ret === 'object' ? ret : obj; // 确保构造器总是会返回一个对象
    };

is

  • ==:等同,比较运算符,两边值类型不同的时候,先进行类型转换,再比较;
  • ===:恒等,严格比较运算符,不做类型转换,类型不同就是不等;
  • Object.is()是ES6新增的用来比较两个值是否严格相等的方法,与===的行为基本一致。

===

这个比较简单,只需要利用下面的规则来判断两个值是否恒等就行了: 如果类型不同,就不相等 如果两个都是数值,并且是同一个值,那么相等; 值得注意的是,如果两个值中至少一个是NaN,那么不相等(判断一个值是否是NaN,可以用isNaN()或Object.is()来判断)。 如果两个都是字符串,每个位置的字符都一样,那么相等;否则不相等。 如果两个值都是同样的Boolean值,那么相等。 如果两个值都引用同一个对象或函数,那么相等,即两个对象的物理地址也必须保持一致;否则不相等。 如果两个值都是null,或者都是undefined,那么相等。

Object.is(),

其行为与===基本一致,不过有两处不同:+0不等于-0。NaN等于自身。举个栗子☺:

1
2
3
4
5
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

==

如果两个值类型相同,进行===比较。 如果两个值类型不同,那么他们可能相等。根据下面规则进行类型转换,然后再比较: 如果一个是null、一个是undefined,那么相等。 如果一个是字符串,一个是数值,把字符串转换成数值再进行比较。 如果任一值是true,把它转换成 1 再比较;如果任一值是false,把它转换成 0 再比较。 如果一个是对象,另一个是数值或字符串,把对象转换成基础类型的值再比较。对象转换成基础类型,利用它的toString或者valueOf方法。 JS的核心内置类,会尝试valueOf先于toString;但有一个是例外——Date,Date利用的是toString转换。 非JS核心的对象,令说(比较麻烦,我也不大懂)。 任何其他组合,都不相等。 举个栗子☺: “1” == true:类型不等,true 会先转换成数值 1 ,现在变成 “1” == 1 ,再把 “1” 转换成 1 ,比较 1 == 1 , 相等。

举个全局的栗子☺:

=:赋值运算符。 ==:等于。 ===:严格等于。 Object.is():加强版严格等于。 例:

1
2
3
4
5
6
var a = 3;
var b = "3";
a==b; // true
a===b; // false,因为*a*,*b*的类型不一样
Object.is( a, b ); //false,因为*a*,*b*的类型不一样


欢迎转载,但转载请注明来自”勿忘初心”的博客,并保持转载后文章内容的完整性。

本文链接:http://lucylc.com/JavaScript/2018/04/16/JavaScript中Object原生方法总结.html

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章