理解JavaScript原型和面向对象(继承)


JavaScript面向对象核心知识归纳:http://duni.sinaapp.com/?p=742

彻底理解JavaScript原型:http://www.imooc.com/article/2088

js构造函数的方法与原型prototype:http://caibaojian.com/js-constructor-vs-prototype.html

关于__proto__和prototype的一些理解:http://www.cnblogs.com/zzcflying/archive/2012/07/20/2601112.html

理解JavaScript作用域和作用域链:http://www.cnblogs.com/dolphinX/p/3280876.html

JavaScript中的this简单易懂:http://www.imooc.com/article/1758

JavaScript中this绑定详解:http://www.html5cn.org/article-9834-1.html

apply和call的应用与区别: http://www.html5cn.org/article-2875-1.html  

如何编写可维护的面向对象JavaScript代码:http://saebbs.com/forum.php?mod=viewthread&tid=37721&page=1&extra=#pid254894

js面向对象[继承]:http://www.w3cfuns.com/notes/22817/d2f3569a8e8a2db7c4d7162e96c0c7d8.html

全面理解面向对象的JavaScript:http://caibaojian.com/javascript-object-2.html#?wb

JavaScript中对象的7种创建方式:http://www.w3cfuns.com/notes/33204/f36b70e9910da3fc4461568fc3806ca1.html

一道常被人轻视的前端JS面试题:http://www.cnblogs.com/xxcanghai/p/5189353.html

前端小白如何用面向对象思想写代码:https://segmentfault.com/a/1190000008993282?utm_medium=hao.caibaojian.com&utm_source=hao.caibaojian.com

 

关于JavaScript原型的概念:

1.所有的对象都有"proto"属性,该属性对应该对象的原型
2.所有的函数对象都有"prototype"属性,该属性的值会被赋值给该函数创建的对象的"proto"属性(当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例,就是设置实例的"proto"属性,也就是说,所有实例的原型引用的是函数的"prototype"属性)
3.所有的原型对象都有"constructor"属性,该属性对应创建所有指向该原型的实例的构造函数

通过一个很简单的例子来了解原型:

function Person(name, age){ //构造函数 
  this.name = name;
  this.age = age;

  this.getInfo = function(){
    console.log(this.name + " is " + this.age + " years old");
    };
}

Person.prototype.Say = function () {
  alert("Person say");
}
var p = new Person("will", 22);
p.Say();
为什么p可以访问Person的Say?
首先var p=new Person(),可以得出p.__proto__=Person.prototype。那么当我们调用p.Say()时,首先p中没有Say这个属性,于是,他就需要到他的__proto__中去找,也就是Person.prototype,而我们在上面定义了Person.prototype.Say=function(){}; 于是,就找到了这个方法。

再来看一个关于原型继承的例子:

// 定义Human类
function Human() {
  this.setName = function (fname, lname) {
  this.fname = fname;
  this.lname = lname;
}
  this.getFullName = function () {
    return this.fname + " " + this.lname;
  };
}

// 定义Human类
function Human() {
  this.setName = function (fname, lname) {
       this.fname = fname;
       this.lname = lname;
   }
  this.getFullName = function () {
    return this.fname + " " + this.lname;
  };
}

//定义Employee类
function Employee(num) {
  this.getNum = function () {
    return num;
  }
};
//让Employee继承Human类
Employee.prototype = new Human();

//实例化Employee对象
var john = new Employee("4815162342");
john.setName("John", "Doe");
alert(john.getFullName() + "'s employee number is " + john.getNum());//John Doe's employee number is 4815162342

构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。 

//组合使用构造函数模式和原型模式
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype = {
  constructor: Person, //更正为正确的指向
  sayName: function(){
    alert(this.name);
  }
};

var person1 = new Person("Nicholas", 29);
person1.sayName();//Nicholas
console.log(person1.age);//29
console.log(person1.constructor);//function Person(){}
console.log(person1.__proto__);//访问person1实例的原型对象
console.log(Person.prototype.constructor);//function Person(){}
console.log(person1.constructor === Person.prototype.constructor);//true

组合继承:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
  alert(this.name);
};

function SubType(name, age){
  //继承属性
  SuperType.call(this, name);
  this.age = age;
}

//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType; //更正为正确的指向
SubType.prototype.sayAge = function(){
  alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas"
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg"
instance2.sayAge(); //27

关于原型的继承:

//原型继承
function A(){
    this.name = 'world';
}

function B(){
    this.bb = "hello";
}

var a = new A();
var b = new B();
 
Object.setPrototypeOf(a, b);//用Object.setPrototypeOf()方法来设置对象的原型,等同于A.prototype = new B();
a.constructor = A;//将b设置为a的原型,此处有一个问题,即a的constructor也指向了B构造函数,可能需要纠正 
console.log(a.constructor);//hello

//ES6中的类继承
class B{
     constructor(){
         this.bb = 'hello'
     }
}
class A extends B{
     constructor(){
        super();
        this.name = 'world';
     }
}
  
var a = new A();
console.log(a.bb + " " + a.name);//hello world
console.log(typeof(A));//"function"