JavaScript 对象操作、继承与模块化实现
1. Object 对象相关方法
Object.getPrototypeOf():返回参数对象的原型,是获取原型对象的标准方法。
var F = function () {};
var f = new F();
Object.getPrototypeOf(f) === F.prototype; // true
Object.setPrototypeOf():为参数对象设置原型,返回该参数对象。可用于模拟new命令。
var a = {};
var b = {x: 1};
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b; // true
Object.create():以指定对象为原型,返回一个新的实例对象。还可接受第二个参数,用于添加实例自身的属性。
var A = { print: function () { console.log('hello'); } };
var B = Object.create(A);
B.print(); // hello
Object.prototype.isPrototypeOf():判断该对象是否为参数对象的原型,只要实例对象处在参数对象的原型链上,就返回true。
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
o2.isPrototypeOf(o3); // true
Object.prototype.__proto__:返回对象的原型,该属性可读写,但不建议使用,推荐用Object.getPrototypeOf()和Object.setPrototypeOf()。
var obj = {};
var p = {};
obj.__proto__ = p;
Object.getPrototypeOf(obj) === p; // true
Object.getOwnPropertyNames():返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名。
Object.getOwnPropertyNames(Date);
Object.prototype.hasOwnProperty():判断某个属性定义在对象自身,还是定义在原型链上。
Date.hasOwnProperty('length'); // true
in运算符:返回一个布尔值,表示一个对象是否具有某个属性,不区分该属性是对象自身的还是继承的。
'length' in Date; // true
for...in循环:用于遍历对象的所有可遍历属性(包括自身和继承的)。
var o1 = { p1: 123 };
var o2 = Object.create(o1, { p2: { value: "abc", enumerable: true } });
for (p in o2) { console.info(p); }
2. 对象的继承
原型对象概述
构造函数的缺点:同一个构造函数的多个实例之间,无法共享属性,造成系统资源浪费。
function Cat(name, color) {
this.name = name;
this.color = color;
this.miaomiao = function () { console.log('喵喵'); };
}
var cat1 = new Cat('大毛', '白色');
var cat2 = new Cat('二毛', '黑色');
cat1.meow === cat2.meow; // false
prototype属性的作用:原型对象的所有属性和方法,都能被实例对象共享。
function Animal(name) { this.name = name; }
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color; // 'white'
原型链:所有对象都有自己的原型对象,形成一个 “原型链”,最终都可以上溯到Object.prototype,其原型是null。
Object.getPrototypeOf(Object.prototype); // null
constructor属性:prototype对象的constructor属性默认指向prototype对象所在的构造函数,可用于得知实例对象的构造函数。修改原型对象时,一般要同时修改constructor属性。
function P() {}
P.prototype.constructor === P; // true
instanceof运算符
返回一个布尔值,表示对象是否为某个构造函数的实例,其实质是检查右边构造函数的原型对象是否在左边对象的原型链上。
var v = new Vehicle();
v instanceof Vehicle; // true
构造函数的继承
分两步实现:
在子类的构造函数中,调用父类的构造函数。
function Sub(value) {
Super.call(this);
this.prop = value;
}
让子类的原型指向父类的原型。
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
多重继承
JavaScript 不提供多重继承功能,但可通过变通方法实现,如 Mixin 模式。
function M1() { this.hello = 'hello'; }
function M2() { this.world = 'world'; }
function S() {
M1.call(this);
M2.call(this);
}
S.prototype = Object.create(M1.prototype);
Object.assign(S.prototype, M2.prototype);
S.prototype.constructor = S;
3. 模块化编程
基本的实现方法
把模块写成一个对象,所有的模块成员都放到这个对象里面,但会暴露所有模块成员。
var module1 = new Object({
_count : 0,
m1 : function (){ /*...*/ },
m2 : function (){ /*...*/ }
});
封装私有变量
构造函数的写法:将私有变量封装在构造函数中,但会导致构造函数与实例对象一体,耗费内存。
function StringBuilder() {
var buffer = [];
this.add = function (str) { buffer.push(str); };
this.toString = function () { return buffer.join(''); };
}
立即执行函数的写法:将相关的属性和方法封装在一个函数作用域里面,可达到不暴露私有成员的目的。
var module1 = (function () {
var _count = 0;
var m1 = function () { /*...*/ };
var m2 = function () { /*...*/ };
return { m1 : m1, m2 : m2 };
})();
模块的放大模式和宽放大模式
放大模式:为模块添加新方法。
var module1 = (function (mod){
mod.m3 = function () { /*...*/ };
return mod;
})(module1);
宽放大模式:适用于无法确定模块加载顺序的情况。
var module1 = (function (mod) { /*...*/ })(window.module1 || {});
输入全局变量
为保证模块的独立性,显式地将其他变量输入模块。
javascript
var module1 = (function ($, YAHOO) { /*...*/ })(jQuery, YAHOO);