JavaScript中没有类的概念,它不是严格意义上的面向对象语言,而是基于对象(Object-based)的编程语言。下面是读《JavaScript高级程序设计(第三版)》的学习笔记,总结一些经常使用的建立对象和继承的方法。html
建立对象最简单的方式就是建立一个Object的实例。经过先建立一个对象,再为它添加属性和方法。此方法用对象字面量方式更为直观:
var animal = { name : "mimi", sayName : function(){ console.log(this.name); } //... }
★ 这种方法在建立多个对象时会产生大量重复的代码,在建立单个对象时的首选模式。编程
这种方法用函数来封装以特定接口建立对象的细节:app
function animal(name) { var o = new Object();//建立新对象 o.name = "mimi";//给这个对象添加属性 //... return o;//返回这个对象 }
★ 这种方法没有解决对象识别的方法,必定程度上解决建立多个类似对象的问题吧,不常使用。编程语言
这种方法建立自定义的构造函数,从而自定义对象类型的属性和方法。函数
function Animal(name) { this.name = name;//将属性和方法赋给this对象 this.sayName = function(){ console.log(this.name); } //... }
它没有显式的建立对象也没有返回语句,但在使用new操做符调用后经历了四个步骤:
(1)建立一个对象
(2)将函数的做用域赋给新对象(this指向了新对象)
(3)执行函数中代码
(4)返回新对象性能
全部对象都有一个constructor属性,指向其构造函数。constructor能够用来标识对象类型,可是,要检测对象类型,instanceof操做符更可靠。学习
★ 这种方法的问题在于,每一个方法都会在每一个实例上从新建立一次。this
function Animal() { } Animal.prototype.name = "mimi";//将方法属性添加到Animal的prototype属性中 Animal.prototype.sayName = function(){ console.log(this.name); }; //...
不管什么时候,建立一个函数,都会自动建立一个prototype属性,指向其原型对象,正如前面所说,每一个对象都有一个constructor属性,指向其构造函数。因此Animal.prototype.constructor指向Animal。判断原型对象与实例间关系可用isPrototypeOf()方法:spa
Animal.prototype.isPrototypeOf(animal1);
判断属性存在于实例中,仍是存在与原型中:
属性存在于实例中时:prototype
animal1.hasOwnProperty(name);//true
属性能经过对象访问:
name in animal1;//true
在实例中添加了与原型中同名的属性
咱们将在实例中建立该属性,而屏蔽原型中的属性。固然,咱们能够经过delete操做符彻底删除实例中的该属性而让咱们从新访问到原型中的属性(*  ̄︿ ̄)。
用字面量来实现更简单的原型语法
function Animal() { } Animal.prototype = { constructor : Animal,//必须必须!由于这样至关于建立了一个新对象并赋值给Animal.prototype, //此时这个新对象的constructor为默认的构造函数Object啊盆友们( ゚Д゚)ノ name : "mimi", sayName : function(){ console.log(this.name); } }
另外,很重要的一点:调用构造函数是会为实例添加一个指向最初原型的[[prototype]]指针,这个链接存在与实例和构造函数的原型对象之间,而不是实例与构造函数间。
咱们将上面将上面的代码稍加修改:
function Animal(name) { } animal1 = new Animal()//建立实例1 console.log(animal1.smell);//undefined;此时原型中还未添加smell属性,理所固然。 Animal.prototype.smell = "good";//添加原型属性 console.log(animal1.smell);//能够访问smell属性。
然而,当用对象字面量来添加原型属性时:
function Animal(name) { } animal1 = new Animal()//建立实例1 console.log(animal1.smell);//undefined;此时原型中还未添加smell属性,理所固然。 Animal.prototype = {//添加原型属性 smell : "good"; } console.log(animal1.smell);//undefined; //由于animal1在建立时[[prototype]]指针指向的是最初的原型,而字面量法添加原型属性时将对象原型改变了,但[[prototype]]没有跟着一块儿变化,因此没法访问。
★ 这个方法的问题在于:1.它没办法传递初始化参数 2.对于引用类型值的属性(A)来讲,改变一个实例的A属性会直接引发全部实例中的A属性的变化,由于实例中的A属性只是其原型中A属性的一个引用。
构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。
function Animal(name) {//添加实例属性 this.name = name; this.color = ["yellow", "black"]; //... } Animal.prototype = {//添加方法和共享属性 constructor : Animal, sayName : function(){ console.log(this.name); } //... }
★ 这个方法就是使用最普遍、认同度最高的建立自定义类型的方法啦。
小总结:第一种和最后一种是比较经常使用的方法了,书中还有提到一些方法,是在前面这些方法都不适用时能够选择使用的,不过我还没碰上啦,因此等之后碰上了再来补充。
JavaScript中不支持接口继承(继承方法签名),都是支持实现继承(继承实际的方法),主要依赖原型链来实现。
将父类的实例赋值给子类的原型,此时,父类的实例中包含一个指向父类原型的指针。子类的原型经过这个指针访问到父类原型中的属性。
function Animal(name) {//父类 this.name = name; } Animal.prototype = {//将属性添加到父类原型 constructor : Animal, sayName : function(){ console.log(this.name); } } function Cat() {//子类 } Cat.prototype = new Animal();//将父类实例赋值给子类原型。 Cat.prototype.constructor = Cat;//prototype被换成另外一个对象,因此constructor属性要重写,不然会指向Animal. var cat1 = new Cat();
其中的原型链:
用instanceof操做符断定可得出cat1是Cat、Animal、Object的实例。
★ 此方法存在问题前面已有说起,就是包含引用类型值的原型属性会被全部实例共享,且不能传参。
在子类型构造函数的内部调用超类型构造函数,经过apply()和call()方法来实现。
function Animal(name) {//父类 this.name = name; } function Cat(name) {//子类 Animal.call(this, name);//执行了一遍父类型构造函数代码 } var cat1 = new Cat();
★ 此方法问题在于:1.方法都在构造函数中定义,函数复用无从谈起。2.在超类的原型中定义的方法对子类型也不可见。并且用instanceof也没法断定cat1与Animal的联系。
使用原型链实现对原型属性和方法的继承,使用借用构造函数实现对实例属性的继承。
function Animal(name) {//父类添加实例属性 this.name = name; } Animal.prototype = {//父类原型添加方法和属性 constructor : Animal, sayName : function(){ console.log(this.name); } } function Cat(name) {//子类 Animal.call(this, name);//继承属性 } Cat.prototype = new Animal();//继承方法 Cat.prototype.constructor = Cat;//将constructor指回子类
★ 此方法是JS中最经常使用的继承模式。并且用instanceof 和isPrototypeOf() 也能识别。
目前有实际用的就这些了,总结的比较基础,但愿也总结清楚了。