-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathobject.js
More file actions
302 lines (241 loc) · 9.73 KB
/
object.js
File metadata and controls
302 lines (241 loc) · 9.73 KB
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/**
* @file Object对象的兼容处理
* @author 龙泉 <yangtuan2009@126.com>
*/
(function(){
/**
* 创建一个具有指定原型的新对象并返回
* 该方法在ECMAScript5中被提出,IE6~8不支持!
* @param {Object} proto 创建新对象使用的原型对象
* @param {Object} [propertiesObject] 设置新对象的私有属性,该参数对应Object.defineProperties()
* @return {Object}
*/
!Object.create && (Object.create = function(proto, propertiesObject) {
if(typeof proto !== 'object' && typeof proto !== 'function') {
throw new Error('Object prototype may only be an Object or null: ' + proto);
}
if(propertiesObject !== undefined) {
throw new Error('This browser\'s implementation of Object.create is a shim and doesn\'t support a second argument.');
}
// 需要注意:
// 因为使用“.prototype =...”后,constructor会改变为“=...”的那个constructor
// 所以在使用Object.create实现继承后往往需要重新指定 .constructor 为自身
function fn() {}
fn.prototype = proto;
return new fn();
});
/**
* 返回目标对象的原型
* 该方法在ECMAScript5中被提出,IE6~8不支持!
* @param {Object} obj 需要返回原型的目标对象
* @return {Object}
*/
!Object.getPrototypeOf && (Object.getPrototypeOf = function(obj) {
if(obj == null) {
throw new Error('Cannot convert undefined or null to object');
}
return '__proto__' in obj ? obj['__proto__'] : obj.constructor.prototype;
});
/**
* 判断两个值是是否是相同的值,与严格等于的区别在于:NaN和NaN同值相等,+0和-0不相等。
* 该方法在ECMAScript6中被提出,浏览器支持:Chrome 30+、Firefox 22+、Safari 9+、Edge+,IE不支持
* @param {AnyType} [x] 需要比较的第一个值。
* @param {AnyType} [y] 需要比较的第二个值。
* @return {Boolean}
*/
!Object.is && (Object.is = function(x, y) {
if(x === y) {
// 处理+0和-0不相等的情况
// 1 / +0 = Infinity
// 1 / -0 = -Infinity
return x !== 0 || 1 / x === 1 / y;
}
// 针对NaN的情况
return x !== x && y !== y;
});
/**
* 返回目标对象所有可遍历属性的键名所组成的数组
* 该方法在ECMAScript5中被提出,IE6~8不支持!
* 该方法在IE9/10的实现中,如果参数不是对象,则会报错!
* @param {Object} obj 目标对象
* @return {Array}
*/
!Object.keys && (Object.keys = function(obj) {
var name, arr = [];
if(obj == null) {
throw new Error('Cannot convert undefined or null to object');
}
else if(typeof obj === 'object' || typeof obj === 'function'){
for(name in obj) {
// 低版本IE浏览器的有些对象不继承Object的hasOwnProperty方法
// 所以需要通过.call的形式调用
if(Object.prototype.hasOwnProperty.call(obj, name)){
arr.push(name);
}
}
}
return arr;
});
/**
* 返回目标对象所有可遍历属性的键值所组成的数组
* 该方法在ECMAScript 2017中被提出,浏览器支持:Chrome 54+、Firefox 47+、Safari 10.1+、Edge+,IE不支持
* @param {Object} obj 目标对象
* @return {Array}
*/
!Object.values && (Object.values = function(obj) {
var name, arr = [];
if(obj == null) {
throw new Error('Cannot convert undefined or null to object');
}
else if(typeof obj === 'object' || typeof obj === 'function'){
for(name in obj) {
if(Object.prototype.hasOwnProperty.call(obj, name)){
arr.push(obj[name]);
}
}
}
return arr;
});
/**
* 返回目标对象所有可遍历属性的键值对数组所组成的数组
* 该方法在ECMAScript 2017中被提出,浏览器支持:Chrome 54+、Firefox 47+、Safari 10.1+、Edge+,IE不支持
* @param {Object} obj 目标对象
* @return {Array}
*/
!Object.entries && (Object.entries = function(obj) {
var name, arr = [];
if(obj == null) {
throw new Error('Cannot convert undefined or null to object');
}
else if(typeof obj === 'object' || typeof obj === 'function'){
for(name in obj) {
if(Object.prototype.hasOwnProperty.call(obj, name)){
arr.push([name, obj[name]]);
}
}
}
return arr;
});
/**
* 将来自一个或多个源对象中的所有可枚举的属性值复制到目标对象,并返回目标对象
* 该方法执行的是浅拷贝,相同属性的值将被后来者覆盖,而不会处理子级对象成员的合并
* 该方法在ECMAScript6中被提出,浏览器支持:Chrome 45+、Firefox 34+、Safari 9+、Edge+,IE不支持
* @param {Object} target 目标对象
* @param {Object} ...sources 一个或多个源对象
* @return {Object}
*/
!Object.assign && (Object.assign = function(target, sources) {
if(target == null) {
throw new Error('Cannot convert undefined or null to object');
}
var name, collection,
argLen = arguments.length, // 参数的长度
index = 1; // 扩展的成员从哪个索引参数开始
// 将基本类型转换为对象
target = Object(target);
// 将需要扩展的成员加入到目标对象
for(; index < argLen; index++){
collection = arguments[index];
collection = typeof collection === 'string' ? collection.split('') : collection;
// 该语句对null、undefined、数字、布尔值不会执行遍历操作,
// string类型是一个可遍历对象,所以这里字符串会被按索引进行合并,
// 但是IE8不支持对字符串的遍历,所以这里先将字符串转换为数组后再做处理
for(name in collection){
if(Object.prototype.hasOwnProperty.call(collection, name)) {
target[name] = collection[name];
}
}
}
// 将被扩展后的目标对象返回
return target;
});
})();
/**
* 将来自一个或多个源对象中的所有可枚举的属性值复制到目标对象,并返回目标对象
* 该方法可根据deep参数决定是否需要进行数组和对象的深度拷贝
* @param {Boolean} [deep] 是否执行深度拷贝,默认为false
* @param {Object} target 目标对象
* @param {Object} ...sources 一个或多个源对象
*/
function extend () {
var name, sourceItem, targetItem, collection, isPlainObject,
args = arguments,
argLen = args.length, // 参数的长度
target = args[0], // 需要扩展成员的目标对象
deep = false, // 是否进行深度合并
index = 1; // 扩展的成员从哪个索引参数开始
if(typeof args[0] === 'boolean'){
deep = args[0];
target = args[1];
index = 2;
}
else if(argLen === 1){
return args[0];
}
// 判断类型是否为:通过{}或者new Object()创建的对象(就是指除内置对象和HTML对象外的自定义对象)
isPlainObject = function (value) {
return value && Object.prototype.toString.call(value).toLowerCase() === '[object object]'
&& value.toString().toLowerCase() === "[object object]";
};
// 将需要扩展的成员加入到目标对象
for(; index < argLen; index++){
collection = args[index];
collection = typeof collection === 'string' ? collection.split('') : collection;
// 该语句对null、undefined、数字、布尔值不会执行遍历操作,
// string类型是一个可遍历对象,所以这里字符串会被按索引进行合并
// 但是IE8不支持对字符串的遍历,所以这里先将字符串转换为数组后再做处理
for(name in collection){
if(Object.prototype.hasOwnProperty.call(collection, name)) {
sourceItem = collection[name];
targetItem = target[name];
// 只针对PlainObject对象和数组进行深度拷贝
if(deep && ((isPlainObject(sourceItem) && isPlainObject(targetItem)) ||
(sourceItem instanceof Array && targetItem instanceof Array))) {
target[name] = extend(deep, targetItem, sourceItem);
}
else {
// null和undefined也将被处理
target[name] = sourceItem;
}
}
}
}
// 将被扩展后的目标对象返回
return target;
}
/**
* 统计目标对象的私有成员的个数,非Object类型返回0。
* 在支持ES5的IE8+等浏览器中可以通过Object.keys(obj).length取得结果
* @param {Object} obj 目标对象
* @return {number}
*/
function countObjectSize (obj) {
var len = 0, name;
if(typeof obj !== 'object') return len;
for(name in obj) {
if(Object.prototype.hasOwnProperty.call(obj, name)) {
len++;
}
}
return len;
}
/**
* 遍历目标对象或数组,针对每个私有成员执行回调函数,回调函数返回false则终止遍历。
* 与for-in语句不同的是,该方法将不会遍历从prototype继承的成员。
* for-in会从原型中查找元素,对于Array.prototype.someProp = function(){}的形式添加的成员,都是可枚举的,都会被for-in遍历到。
* 对于支持ES5的IE8+等浏览器中,可以通过Array.prototype.forEach()来进行私有成员的遍历。
* 对于支持ES6 Iterator(遍历器)结构的现在浏览器,可以使用for...of进行私有成员的遍历。
* @param {Object|Array} obj 对象或数组
* @param {Function} callback 回调函数 callback(val, index, obj)
* @return {undefined}
*/
function each(obj, callback) {
if(typeof obj !== 'object' || typeof callback !== 'function') return;
for(var name in obj) {
if(Object.prototype.hasOwnProperty.call(obj, name)) {
if (callback(obj[name], name, obj) === false) {
break;
}
}
}
}