javascript Object 总结 

timg.jpeg

作为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的几个重要方法:

1defineProperty

通过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' } } }

对象属性完全冻结,大功告成!

发表评论

登录 后参与评论

评论列表 (1条)

  • yuwenbin
    2 个月前
    GOOD!