
作为JavaScript中非常重要的一部分Object对象,涵盖了很多基础知识,下面就重点总结一下:
一、对象的创建
1、工厂方法创建:
我们可以通过声明函数方式创建:
function createUser(name) {
let dna = {
name,
printf: function () {
console.log(this.name);
}
}
return dna;
}使用:
let user1 = createUser('dora');
user1.printf();这样,我们创建了一个User对象,然后执行了对象中的printf方法。
2、面向对象方式创建(原型链对象)
对象属性分为两部分:
① 属性个性化/个性化属性:
function User(name, id) { // 对生成的构造器,就是生成对象个性化属性的
if (name == 'dora') {
this.name = name;
}
this.id = id;
}② 原型链属性/共有属性
User.prototype.printf = function () {
console.log(this.name, this.id);
}使用:
let user1 = new User('dora', '0617251');
user1.printf();二、对象的继承
1、原型链需要注意的。
我们通过原型链方式创建对象可以这么写:
function User(name) {
this.name = name;
}
User.prototype.changename = function () {
}
User.prototype.changeage = function () {
}我们赋予了User对象的属性和方法。有没有更简洁的方式来写对象方法呢,你可能很容易想到:
User.prototype = {
changename = function () {
},
changeage = function () {
}
}答案是不可以,因为对象的原型链有默认属性constructor,上面的写法无形把constructor漏掉了,或许可以这么写:
User.prototype = {
constructor : User
changename = function () {
},
changeage = function () {
}
}但是一般情况下,我们还是用上面的第一种写法。
2、构造函数上的个性属性和原型链上的方法名是可以重复的,我们看下面的栗子:
function User(name) {
this.name = name;
this.changname = function () {
console.log(123);
}
}
User.prototype.changname = function () {
console.log(this.name);
}
let u = new User('cms 233');
u.changname();这种情况下会输出什么呢?答案是 123 。遵循原则:先从自身方法开始找 -> 原型链,那是不是说明自身方法覆盖了原型链的方法呢?
执行:
console.log(u.__proto__.changname.toString());
输出:
function () {
console.log(this.name);
}说明方法存在,但是没执行,只是优先执行了自身构造函数里的方法。
3、原型链继承。把握两点:
① 继承个性属性
② 继承原型链方法
举个栗子:
function People(id) {
this.id = id;
}
People.prototype.change = function () {
console.log('test me');
}
function User(name, id) {
// 继承个性属性
People.call(this, id);
this.name = name;
}
// 继承方法
User.prototype.__proto__ = People.prototype;
let u = new User('dora', '0001');
u.change();
console.log(u.id);这样,User 继承了 People 的相关属性和方法。
4、了解了基本的对象继承,我们可以自己写一个方法来实现对象的继承
const createClass = require('./createClass');
const People = createClass({ // extends Object
construct: function (name) {
this.name = name;
},
changeName: function (name) {
this.name = name
}
})
const User = createClass({ // extends People
construct: function (name, id) {
this.super(name);
this.id = id;
},
changeId: function (id) {
this.id = id;
}
}, People);
const u = new User('dora', '112000');
u.changeName('dora1');
console.log(u.name, u.id);我们通过createClass方法来创建对象,可以通过传参方式实现对象继承。
createClass 伪代码实现:
function createClass(obj = {}, SuperClass = {}) {
let newClass = obj.construct;
newClass.prototype = {};
for (let props in obj) {
if (props != 'construct') {
newClass.prototype[props] = obj[props];
}
}
newClass.prototype.super = function (agv) {
console.log('----agv------', agv);
}
if (SuperClass) {
newClass.prototype.__proto__ = SuperClass.prototype;
}
return newClass;
}
module.exports = createClass;原理: 通过获取被继承对象的原型链属性和方法,赋值到当前对象上。
三、关于Object的几个重要方法:
1、defineProperty
通过Object.defineProperty方法可以对对象属性进行定义:
var obj = {
name: 'dora',
_type: ''
}
Object.defineProperty(obj, 'type', {
configurable: true,
// enumerable: true,
// writable: true,
value: "User"
get: function () {
console.log('getter method');
return this._type
},
set(v) {
console.log('setter method');
this._type = v;
}
});这里只说明defineProperty第三个参数:
① configurable : 属性可配置,默认为false,当为true时,执行
delete obj.type; console.log(obj.type);
输出
undefined
② enumerable:是否可枚举,默认为false
执行
console.log(obj);
输出
{ name: 'dora', _type: 'People' }设置为true
{ name: 'dora', _type: 'People', type: [Getter/Setter] }③ writable:是否可写,这个属性允许或阻止属性是否可更改。
2、创建多属性defineProperties:
var o = {
_type: ''
};
Object.defineProperties(o, {
"name": {
configurable: true,
get() {
return 'ok'
}
},
'type': {
enumerable: true,
get() {
return this._type;
},
set(v) {
this._type = v
}
}
})
o.type = 'yesno'
console.log(o.type);跟上面的创建单属性有一点区别,但不大。
3、数据的深浅拷贝
① 简单的浅拷贝只用给新变量赋值就可以了:
var o = {
k: {
name: 'doramart'
}
}
var o2 = {
}
o2.k = o.k;
console.log(o2.k === o.k);改操作只是将o指针指向o2,对象o 和 o2是全等的,浅拷贝的问题在于,当o2的属性值发生变化,o的属性值也会发生改变。
② 深拷贝。我们可以通过将对象字面量串行化方式实现:
let ostr = JSON.stringify(o); let o3 = JSON.parse(ostr); console.log(o3 === o);
此时的o3是一个全新的对象,和对象o再无关联。
4、对象的冻结。
Object 提供了三种方法对对象属性进行不同程度的冻结:
Object.preventExtensions(obj) : 无法对对象增加新属性
Object.seal(obj) : 无法对对象进行删除操作
Object.freeze(obj) : 冻结(浅层),除了上面两种外,对浅层的对象属性无法更改:
var obj = {
name: 'dora',
sub: {
qq: '443322222',
result: {
ok1: 'good',
ok2: 'success'
}
}
}
Object.freeze(obj); // 冻结(浅层)
delete obj.name;
obj.type = 'js';
obj.name = 'dora1';
obj.sub.qq = 'what salei';
obj.sub.result.ok1 = 'really ok?';
console.log(obj);输出:
{ name: 'dora',
sub:
{ qq: 'what salei',
result: { ok1: 'really ok?', ok2: 'success' } } }我们发现 name 属性改变不了,但是 sub 属性的自属性 qq,result.ok1改变了。如何实现对象是深层冻结呢,官方没提供方法,我这里自己实现了一下:
function deepFreeze(obj) {
let getSubObj = (subObj) => {
Object.freeze(subObj);
for (let prop in subObj) {
if (typeof subObj[prop] === 'object') {
Object.freeze(subObj[prop]);
getSubObj(subObj[prop]);
} else {
continue;
}
}
}
getSubObj(obj);
}执行
deepFreeze(obj)
输出
{ name: 'dora',
sub: { qq: '443322222', result: { ok1: 'good', ok2: 'success' } } }对象属性完全冻结,大功告成!