javascript Object 总结
作为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' } } }
对象属性完全冻结,大功告成!