全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/13714848.html, 多谢,=。=~(如果对你有帮助的话请帮我点个赞啦)
重新学习JavaScript是因为当年转前端有点儿赶鸭子上架的意味,我一直在反思我的知识点总是很零散,不能在脑海中形成一个完整的体系,所以这次想通过再次学习将知识点都串联起来,结合日常开发的项目,达到温故而知新的效果。与此同时,总结一下我认为很重要但又被我遗漏的知识点~
Object()
方法
可以当作工具方法使用,将任意值转为对象,如果参数为空(或者为undefined
和null
),返回一个空对象;如果参数是原始类型的值,会转为对应的包装对象;如果参数是一个对象,则直接返回该对象(特殊用法,用于判断变量是否为对象)。
var obj = Object(1);
obj instanceof Object // true
obj instanceof Number // true
// 判断变量是否为对象
function isObject(value) {
return value === Object(value);
}
isObject([]) // true
isObject(true) // false
Object
的静态方法
指部署在Object
对象自身的方法。
Object.keys()
和Object.getOwnPropertyNames()
参数为一个对象,返回一个数组,数组为该对象自身的(而不是继承的)所有属性名,区别是前者只返回可枚举(enumerable: true
)的属性,由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。
var a = ['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
var obj = {
p1: 123,
p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2
// 获取对象的所有属性(不管是自身的还是继承的,也不管是否可枚举)
function inheritedPropertyNames(obj) {
var props = {};
while(obj) {
Object.getOwnPropertyNames(obj).forEach(function(p) {
props[p] = true;
});
// 依次获取obj对象的每一级原型对象“自身”的属性,从而获取obj对象的“所有”属性,不管是否可遍历。
obj = Object.getPrototypeOf(obj);
}
return Object.getOwnPropertyNames(props);
}
Object.getOwnPropertyDescriptor()
获取某个属性的描述对象(属性描述对象说明见下文),第一个参数是目标对象,第二个参数是一个字符串,对应目标对象的某个属性名,只能用于对象自身的属性,不能用于继承的属性。
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
// }
Object.defineProperty()
通过描述对象(属性描述对象说明见下文),定义或修改某个属性,然后返回修改后的对象,参数:属性所在对象、属性名字符串、属性描述对象。
var obj = Object.defineProperty({}, 'p', {
value: 123,
writable: false, // 如果原型对象的某个属性的writable为false,那么子对象将无法自定义这个属性,但可以通过defineProperty修改value来绕过限制。
enumerable: true,
configurable: false
});
obj.p // 123
obj.p = 246; // 正常模式下只是默默失败,严格模式(use strict)下会报错
obj.p // 123
Object.defineProperties()
通过描述对象(属性描述对象说明见下文),定义或修改多个属性,然后返回修改后的对象,参数:属性所在对象、属性名与属性描述对象的键值对对象。
var obj = Object.defineProperties({}, {
p1: { value: 123, enumerable: true },
p2: { value: 'abc', enumerable: true },
p3: { get: function () { return this.p1 + this.p2 },
enumerable:true,
configurable:true
}
});
obj.p1 // 123
obj.p2 // "abc"
obj.p3 // "123abc"
Object.create()
可以指定原型对象(参数不能为空且必须是对象)和属性,返回一个新的对象(可以实现由一个实例对象生成另一个实例对象)。
// 原型对象
var A = {
print: function () {
console.log('hello');
}
};
// 实例对象
var B = Object.create(A);
Object.getPrototypeOf(B) === A // true
B.print() // hello
B.print === A.print // true
// 传入属性描述对象参数
var obj = Object.create({}, {
p1: {
value: 123,
enumerable: true,
configurable: true,
writable: true,
},
p2: {
value: 'abc',
enumerable: true,
configurable: true,
writable: true,
}
});
// 等同于
var obj = Object.create({});
obj.p1 = 123;
obj.p2 = 'abc';
生成一个不继承任何属性(比如没有toString
和valueOf
方法)的对象。
var obj = Object.create(null);
obj.valueOf()
// TypeError: Object [object Object] has no method 'valueOf'
Object.getPrototypeOf()
获取对象的Prototype对象,即原型。
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype // true
// 空对象的原型是 Object.prototype
Object.getPrototypeOf({}) === Object.prototype // true
// Object.prototype 的原型是 null
Object.getPrototypeOf(Object.prototype) === null // true
// 函数的原型是 Function.prototype
function f() {}
Object.getPrototypeOf(f) === Function.prototype // true
Object.setPrototypeOf()
为参数对象设置原型,返回该参数对象。它接受两个参数,第一个是现有对象,第二个是原型对象。
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b // true
a.x // 1
Object
的实例方法
指定义在Object.prototype
对象上的方法,所有Object
的实例对象都继承了这些方法。
Object.prototype.valueOf()
返回当前对象对应的值,默认情况下返回对象本身,主要用途是JavaScript自动类型转换时会默认调用这个方法。
var obj = new Object();
1 + obj // "1[object Object]"
Object.prototype.toString()
返回当前对象对应的字符串形式,默认情况下返回类型字符串"[object object]"
(第二个值表示该对象的构造函数),数值、数组、字符串、函数、Date 对象都分别部署了自定义的toString
方法,覆盖了原生的Object.prototype.toString
方法。
var o = {a:1};
o.toString() // "[object Object]"
// 数值
(10).toString() // "10"
// 数组
[1, 2, 3].toString() // "1,2,3"
// 字符串
'123'.toString() // "123"
// 函数
(function () {
return 123;
}).toString()
// "function () {
// return 123;
// }"
// Date对象
(new Date()).toString()
// "Tue May 10 2016 09:11:31 GMT+0800 (CST)"
特殊应用:用于判断数据类型,由于实例对象可能会自定义toString
方法,覆盖掉Object.prototype.toString
方法,所以可以利用call
直接调用原型方法。
Object.prototype.toString.call(value)
//数值:返回[object Number]
//字符串:返回[object String]
//布尔值:返回[object Boolean]
//undefined:返回[object Undefined]
//null:返回[object Null]
//数组:返回[object Array]
//arguments 对象:返回[object Arguments]
//函数:返回[object Function]
Object.prototype.toString.call(Math) // "[object Math]"
//Error 对象:返回[object Error]
//Date 对象:返回[object Date]
//RegExp 对象:返回[object RegExp]
//其他对象:返回[object Object]
//一个比typeof运算符更准确的类型判断函数
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/[object (.*?)]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
Object.prototype.toLocaleString()
返回当前对象对应的本地字符串形式,主要作用是留出一个接口,让各种不同的对象实现自己版本的toLocaleString
,用来返回针对某些地域的特定的值,目前Array
、Number
、Date
自定义了toLocaleString
方法。
var person = {
toString: function () {
return 'Henry Norman Bethune';
},
toLocaleString: function () {
return '白求恩';
}
};
person.toString() // Henry Norman Bethune
person.toLocaleString() // 白求恩
var date = new Date();
date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
date.toLocaleString() // "1/01/2018, 12:01:33 PM"
Object.prototype.hasOwnProperty()
判断某个属性是否为当前对象自身的属性,还是继承自原型对象的属性。
var obj = {
p: 123
};
obj.hasOwnProperty('p') // true
obj.hasOwnProperty('toString') // false
Object.prototype.isPrototypeOf()
判断当前对象是否为另一个对象的原型。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3) // true
o1.isPrototypeOf(o3) // true
Object.prototype.isPrototypeOf(/xyz/) // true
Object.prototype.isPrototypeOf(Object.create(null)) // false
Object.prototype.propertyIsEnumerable()
判断某个属性是否可枚举,只能用于判断对象自身的属性,对于继承的属性一律返回false。
var obj = {};
obj.p = 123;
obj.propertyIsEnumerable('p') // true
obj.propertyIsEnumerable('toString') // false
属性描述对象
JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自己对应的属性描述对象,保存该属性的一些元信息。
PS:value
+writable:true
属性与get
+set
不能共存,在Object.defineProperty()
和Object.defineProperties()
参数里面的属性描述对象,writable
、configurable
、enumerable
这三个属性的默认值都为false
。
// 属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性
{
value: 123, // 属性值,默认为undefined
writable: false, // 布尔值,表示属性值(value)是否可改变(即是否可写),默认为true
enumerable: true, // 布尔值,表示该属性是否可遍历,默认为true(不可遍历时for...in循环、Object.keys()、JSON.stringify会跳过该属性)
configurable: false, // 布尔值,表示可配置性,默认为true,控制了属性描述对象的可写性(不可配置时无法删除该属性,也不得改变该属性的属性描述对象(value属性在writable为true时除外,writable的true改false除外))
get: undefined, // 表示该属性的取值函数(getter),默认为undefined,取值时会调用
set: undefined // 表示该属性的存值函数(setter),默认为undefined,存值时会调用
}
- 存取器
setter
和getter
存值函数称为setter
,使用属性描述对象的set
属性;取值函数称为getter
,使用属性描述对象的get
属性。
// 写法一(enumerable、configurable默认为false)
var obj = Object.defineProperty({}, 'p', {
get: function () {
return 'getter';
},
set: function (value) {
console.log('setter: ' + value);
}
});
obj.p // "getter"
obj.p = 123 // "setter: 123"
// 写法二(更推荐,因为enumerable、configurable默认为true)
var obj = {
get p() {
return 'getter';
},
set p(value) {
console.log('setter: ' + value);
}
};
对象的拷贝
将一个对象的所有属性,拷贝到另一个对象,为了能把存取器定义的属性也成功拷贝,可以使用以下方法。
var extend = function (to, from) {
for (var property in from) {
if (!from.hasOwnProperty(property)) continue; // 过滤掉继承的属性
Object.defineProperty(
to,
property,
Object.getOwnPropertyDescriptor(from, property)
);
}
return to;
}
extend({}, { get a(){ return 1 } })
// { get a(){ return 1 } })
控制对象状态
有时需要冻结对象的读写状态,防止对象被改变(但有漏洞:可以通过改变原型对象,来为对象增加属性;如果属性值是对象,就只能冻结属性指向的对象(即无法指向其他值),而不能冻结对象本身的内容)。JavaScript 提供了三种冻结方法,最弱的一种是Object.preventExtensions
,其次是Object.seal
,最强的是Object.freeze
。
Object.preventExtensions()
可以使得一个对象无法再添加新的属性。
var obj = new Object();
Object.preventExtensions(obj);
Object.defineProperty(obj, 'p', {
value: 'hello'
});
// TypeError: Cannot define property:p, object is not extensible.
obj.p = 1;
obj.p // undefined
Object.isExtensible()
用于检查一个对象是否使用了Object.preventExtensions
方法,也就是说,检查是否可以为一个对象添加属性。
var obj = new Object();
Object.isExtensible(obj) // true
Object.preventExtensions(obj);
Object.isExtensible(obj) // false
Object.seal()
使得一个对象既无法添加新属性,也无法删除旧属性,实质是把属性描述对象的configurable
属性设为false
。
var obj = { p: 'hello' };
Object.seal(obj);
delete obj.p;
obj.p // "hello"
obj.x = 'world';
obj.x // undefined
Object.isSealed()
用于检查一个对象是否使用了Object.seal
方法。
var obj = { p: 'a' };
Object.seal(obj);
Object.isSealed(obj) // true
Object.isExtensible(obj) // false
Object.freeze()
可以使得一个对象无法添加新属性、无法删除旧属性、也无法改变属性的值,使得这个对象实际上变成了常量。
var obj = {
p: 'hello'
};
Object.freeze(obj);
obj.p = 'world';
obj.p // "hello"
obj.t = 'hello';
obj.t // undefined
delete obj.p // false
obj.p // "hello"
Object.isFrozen()
用于检查一个对象是否使用了Object.freeze
方法。
var obj = {
p: 'hello'
};
Object.freeze(obj);
Object.isFrozen(obj) // true
Object.isExtensible(obj) // false
参考资料
JavaScript 语言入门教程 :https://wangdoc.com/javascript/index.html