1.概述
js基于构造函数(constructor)和原型链(prototype)
构造函数:
作用:提供模板,描述对象的基本结构
var Vehicle=function(a){
this.price=a;
}
- 函数体内部使用了this
- 生成对象时必须使用new命令
- 为表示区别,第一个字母通常大写
new:
作用:执行构造函数,返回一个实例对象
var v=new Vehicle(); //也可不带括号
忘记使用new命令直接调用构造函数
var v=Vehicle(); v.price //v变成了undefined price //price为全局变量
为避免这种情况,可以在构造函数内部第一行加上
"use strict"
(严格模式下this
不能指向全局对象,默认等于undefined
,由于不能对undefined
添加属性,导致不加new
调用会报错);
另外还可以在构造函数内部加一个判断
if(!(this instanceof 函数名)){ return new 函数调用; }
如果构造函数内部有return语句,若return返回一个数值,new命令会忽略return语句,返回构造后的this对象。
- 如果返回的是一个与this无关的新对象,new命令返回新对象不返还this对象
- 若对普通函数(内部没有this关键字)使用new命令,会返回一个空对象
new的原理:
- 创建一个空对象
- 把空对象的原型指向构造函数的prototype属性
- 把空对象赋值给函数内部的this关键字
- 开始执行构造函数内部代码
2.this关键字
this
总是返回属性或方法当前所在的对象(比如在setTimeout()
的回调函数中使用this
会指向它所在的对象window
),this
指向可人为改变,函数在全局环境下运行时this
指向顶层对象window
。
详见原文
绑定this:
方法 | 用法 |
---|---|
function.prototype.call(obj[,arg1,arg2,...]) | f内部的this指向obj,若参数为空、null、undefined,默认传入全局对象,若参数是一个原始值(如5等),则这个原始值自动转成对应的包装对象(如Number的实例),可以在覆盖掉继承的方法后,用call来调用对象的原生方法 |
function.prototype.apply(obj[, [arg1, arg2, ...]]) | 与call类似,但是——如果要传入参数,必须以数组形式传入,应用见原文,一定要见! |
function.prototype.bind(obj[,arg1,arg2,...]) | 比call和apply更进一步,不仅可以绑定this,还可以绑定原函数的参数,每一次运行都会返回一个新函数,这些话不懂的话见原文,注意事项也要见原文,一定要见! |
3.Object对象与继承
各种方法:
方法 | 用法 |
---|---|
Object.getOwnPropertyNames(obj) | 以数组形式返回obj本身的所有属性的键名(不包括继承的属性) |
Object.keys(obj) | 与上面的类似,但是只返回可枚举的属性键名 |
Object.prototype.hasOwnProperty('属性名') | 返回一个Boolean值,判断某个属性定义在对象自身还是原型链上,是js中唯一一个处理对象属性时不会遍历原型链的方法 |
in运算符和for…in循环:
/ | 示例 | 用法 |
---|---|---|
in | '属性名' in obj | 返回一个Boolean值,表明obj是否具有该属性,不区分是否是继承来的属性 |
for...in | 暂时不明 | 获取对象的所有可枚举属性,不区分是否继承而来,详情见原文 |
对象的拷贝:
见原文
4.prototype 对象
概述:
- JavaScript的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。null除外,它没有自己的原型对象。
- 通过构造函数生成实例对象时,会自动为实例对象分配原型对象。每一个构造函数都有一个prototype属性,这个属性就是实例对象的原型对象。当实例对象本身没有某个属性或方法的时候,它会到构造函数的prototype属性指向的对象,去寻找该属性或方法。总结一下,原型对象的作用,就是定义所有实例对象共享的属性和方法。
- 原型对象上的所有属性和方法,都能被派生对象共享。这就是JavaScript继承机制的基本设计。
构造函数的缺陷:
实例之间不能共享属性,如果在构造函数中定义
this.func: ...
,每新建一个实例就新建一个方法,没有必要且浪费系统资源;
原型链:
- 对象的属性和方法,有可能是定义在自身,也有可能是定义在它的原型对象。由于原型本身也是对象,又有自己的原型,所以形成了一条原型链(prototype chain)。所有对象的原型最终都可以上溯Object.prototype;
Object.prototype
的原型就是没有任何属性和方法的null
对象,null
对象没有自己的原型。
详情见原文
constructor属性:
prototype对象有一个
constructor
属性,默认指向prototype对象所在的构造函数。
修改了prototype以后,constructor
属性的指向就变了,导致instanceof
运算符失真。所以,修改原型对象时,一般要同时校正constructor
属性的指向。
// 避免这种写法
C.prototype = {
method1: function (...) { ... },
// ...
};
// 较好的写法
C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...
};
// 好的写法
C.prototype.method1 = function (...) { ... };
此外,通过
name
属性,可以从实例得到构造函数的名称,如:f.constructor.name
instanceof运算符:
instanceof
运算符返回一个布尔值,表示指定对象是否为某个构造函数的实例。- 由于
instanceof
对整个原型链上的对象都有效,因此同一个实例对象,可能会对多个构造函数都返回true。instanceof
的原理是检查原型链,对于那些不存在原型链的对象,就无法判断。- instanceof运算符只能用于对象,不适用原始类型的值。
各种方法:
方法 | 用法 |
---|---|
Object.getPrototypeOf(obj) | 返回一个对象的原型 |
Object.setPrototypeOf(现有obj,原型对象) | 为现有对象设置原型,返回一个新对象 |
Object.create(obj) | 用于从原型对象生成新的实例对象,生成的新对象动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上 |
Object.prototype.isPrototypeOf() | 对象实例的isPrototypeOf方法,用来判断一个对象是否是另一个对象的原型。 |
Object.create()的其他用法:
除了对象的原型,
Object.create
方法还可以接受第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到新对象。只要某个对象处在原型链上,isProtypeOf都返回true。
var o = Object.create({}, {
p1: { value: 123, enumerable: true },
p2: { value: 'abc', enumerable: true }
});
// 等同于
var o = Object.create({});
o.p1 = 123;
o.p2 = 'abc';
Object.prototype._proto_:
__proto__属性可以改写某个对象的原型对象。
5.面向对象编程的模式
暂时不写了,见原文吧