文章目录
一、继承
1、概念
- f.prototype原型:函数的原型,当用函数f作为构造函数创造实例时,实例instance会继承f.prototype;
- obj.__ proto__原型对象:一种浏览器支持的非标准方式;obj可以为任何对象,obj2.__ proto__=obj;就直接表示obj2继承自obj。
####2、继承的语法
- ES5定义了obj2 = Object.create(obj)方法来实现继承关系;调用此函数后obj2继承自obj;
- 针对没有Object.create()方法的ES3,我们定义了下面inherit函数;通过这个函数的实现,可以看出继承的关键:
- 首先定义一个构造函数f;
- f.prototype = p;f的原型为p
- 构造f的实例new f();
- obj2.__ proto=obj;可以直接实现obj2继承自obj的语法
A继承了B,则:
- 如果在A中查询属性x,如果A中不存在,则在A的原型对象B中查找;如果B中也没有,则在B的原型对象中查找,以此类推,直到原型对象链结束。
- 如果给A中属性x赋值:
- 如A中有已有x(不是继承来的),那么就直接改变这个x的值(受writable特性的限制);
- 如A中没有x,而其原型对象链中有(且可写),那么A中添加x,并给它赋值;而它会覆盖原型对象链中的x(以后通过A访问x都是这个x);
- 如A中没有x,而其原型对象链中有(但不可写),赋值操作不造成任何影响;
- 对getter和setter进行操作时,writable特性,是依据getter和setter内部修改的实际属性。
- 查询操作会受继承的影响,赋值操作只是对对象本身的修改(当原型对象链中有不可修改的属性时,对象中不能添加此属性);
/***********************************************
* inherit()返回一个继承自原型对象p的属性的新对象
* 这里使用ECMAScript 5中的Object.create()函数(如果存在的话)
* 如果不存在Object.create()方法,则退化使用其他方法
***********************************************/
function inherit(p) {
if (Object.create) return Object.create(p); //使用Object.create方法
/* 检测p的类型是否符合要求 */
var t = typeof p;
if (t !== 'object' && t !== 'function') throw TypeError();
/* 下面的原型创建方法,类似于Object.create(p); */
function f() {
};
f.prototype = p;
return new f();
}
/***
* 此例中定义了对象a,具有可修改的属性x和存取器y
* 此例中定义了对象b,具有不可修改的属性x和存取器y
* c继承自a,d继承自b;调用c和d的setter后,观察对象getter返回值的变化
* */
//a有属性x(可读写),存取器属性y(可读写)
var a = {
x: 1 };
Object.defineProperty(a, 'y', {
get: function() {
return this.x + 1;
},
set: function(x) {
this.x = x - 1;
},
configurable: true,
});
//b有属性x(不可写),存取器属性y(可读写)
var b = {
};
Object.defineProperty(b, 'x', {
value: 1, configurable: true });
Object.defineProperty(b, 'y', {
get: function() {
return this.x + 1;
},
set: function(x) {
this.x = x - 1;
},
configurable: true,
})
var c = Object.create(a);
c.y = 3;
var d = Object.create(b);
d.y = 3;
//由于a中x可读写,所以给c添加一个熟悉x;而b中x不可写,调用setter没有任何效果
console.log(a.y); //2
console.log(c.y); //3
console.log(b.y); //2
console.log(d.y); //2
二、类
1、JavaScript中的类型
JavaScript中只有两大类型:原始值和对象;类型通过typeof运算符来得到
1.1、原始值
原始值的特点是不可修改(在那个特定的内存区域保存的就是这个原始值的值);我们可以让变量指向另一个原始值,但无法改变原始值内部的内容。
- undefined
- null
- Number
- Boolean
- String
- Symbol: 一种特殊的string序列(独一无二)
#####1.2、对象 - 对象本身就是一个引用,它指向一个集合的地址,集合内是对象的属性;我们修改集合内的对象属性,不改变对象的值,只有当我们将对象指向另一个集合时,才会改变对象的值。
- JavaScript作为一个弱类型的语言,它的对象类型非常灵活。面对如此庞杂的对象,我们要识别它,就引入了类的概念。
####2、类的概念
#####2.1、类的基本组成
类,顾名思义,就是对对象进行分类和抽象,它主要有下面几部分的内容: - 实例字段:它们是基于实例的属性或变量,用以保存对立对象的状态;在js中,就是构造函数中this的属性。
- 实例方法:他们是类的所有实例所共享的方法,由每个独立的实例调用;在js中,通过原型来实现,就是constructor.prototype的方法;
- 类字段: 这些属性或变量属于类的,而不是属于类的某个实例的;在js中,就是constructor.properties;
- 类方法: 这些方法属于类的,而不是属于类的某个实例的;在js中,就是constructor.methods;
上面的2、3、4项,都是为了实现某种类型的共享。区别主要有:实例方法,可以在函数内部直接使用this来访问实例的属性和方法;而类的方法和字段,类似于全局函数和变量,作为构造函数的属性,只是在标识符上用类名做了一个区分。
2.2、类的各部分关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M1IQ1eJb-1593672875475)(./asset/proto.jpg)]
以Persion类为例,令 proto = Persion.prototype;类需要具备以下条件:
- proto.constructor = Persion;
- Persion.prototype = proto;
- instance = Object.create(proto);在构造函数里,我们通过new来实现这个关系,在工厂函数中,我们可以通过手动实现。
上面的第三条是类的根本,要检验 instance instanceof Persion 实际上就是 instance的继承链上是否有Persion.prototype,无论是不是直接继承。
/**factory method
* 一个简单的范围类
*/
function range(from, to) {
var r = Object.create(range.prototype);//上面的条件3
r.from = from;
r.to = to;
return r;
}
range.prototype = {
//上面的条件2
constructor: range, //上面的条件1
includes: function(x) {
return this.from <= x && x <= this.to;
},
foreach: function(f) {
for (var x = Math.ceil(this.from); x <= this.to; ++x) f(x);
},
toString: function() {
return '(' + this.from + '...' + this.to + ')';
}
};
var r = range(1, 3);
console.log(r instanceof range);//true;满足了上面三个条件,所以r是range的实例
2.3、类的各部分关系的再深入
javaScript中,类主要由constructor、constructor.prototype两部分组成。constructor有两部分:构造函数本身,创建instance数据;constructor的属性是类的静态方法和数据
/**一个定义简单类的函数 */
/**extend
* 拓展对象o中的属性(只处理可枚举属性,包括继承来的属性)
* 如果o与p中有同名属性,则覆盖o中属性
* @param {Object} o
* @param {Object} p
* @return {Object}
*/
function extend(o, p) {
for (var prop in p) {
o[prop] = p[prop]; //浅复制
}
return o;
}
/**defineClass
* 通过构造函数constructor、实例方法method、类数据和类方法static来构造类
* @param {Function} constructor
* @param {Object} method
* @param {Object} static
* @return {Function}
*/
function defineClass(constructor, method, static) {
if (method) extend(constructor.prototype, method);
if (static) extend(constructor, static);
return constructor;
}
//constructor
function Complex(real, imaginary) {
if (isNaN(real) || isNaN(imaginary)) throw TypeError("real and imaginary must be number!");
this.r = real;
this.i = imaginary;
}
//methods