1.封装
// 面向对象编程-类的封装 // 类中的属性和方法: // 私有属性 // 私有方法 // 公有属性 // 特权方法 // 原型属性(位于原型中) // 公有方法(位于原型中) // 寄生组合式继承 // 父类 function Person(name, age) { // 公有属性 this.name = name; this.age = age; // 特权方法 // 特权方法可以访问私有属性、私有方法、特权属性、特权方法 // 特权方法不能放在prototype内,因此无法复用 // 访问私有属性sexOrientation的特权方法 this.getSexOrientation = function () { return sexOrientation; }; // 访问私有方法setSexOrientation的特权方法 this.admittedByBUPT = function () { console.log("I'm admitted by BUPT!"); setSexOrientation('同性恋'); }; // 私有属性 var sexOrientation = '异性恋'; // 私有方法 // 私有方法可以访问私有属性(闭包)、私有方法(闭包) // 因为在私有方法中this指向的是全局对象window(不信可以打印this看一下),所以无法通过this访问到公有属性和特权方法 var setSexOrientation = function (sex) { sexOrientation = sex; // console.log(this); }; } // 原型属性 Person.prototype.className = 'Person'; // 原型方法 // 原型方法可以访问原型属性、原型方法、公有属性、特权方法,但不可以访问私有属性和私有方法 Person.prototype.getName = function () { return this.name; }; Person.prototype.getAge = function () { return this.age; }; // 静态属性 Person.country = 'Chinese'; // 静态方法 Person.setCountry = function (country) { this.country = country; }; Person.getCountry = function () { return this.country; }; // 寄生组合式继承 // 继承公有属性、特权方法、原型属性、公有方法 // 本写法是纯净的组合式继承 // 组合式继承指的是“原型链继承”+“构造函数式继承” function Teacher(name, age, school) { // 构造函数式继承 Person.call(this, name, age); // 子类新增公有属性 this.school = school; } // 子类新增公有方法 Teacher.prototype.getSchool = function () { return this.school }; // 实现继承 // 可以将这段实现继承的代码封装成一个inherit函数 var F = function () {}; F.prototype = Person.prototype; var o = new F(); o.constructor = Teacher; Teacher.prototype = o;
2.原型链继承
// 原型链继承 // 父类 function Person(name,age) { this.name = name; this.age = age; } Person.prototype.getName = function () { return this.name; } Person.prototype.getAge = function () { return this.age; } // 子类 function Teacher(school) { this.school = school; } // 注意 // 创建继承关系时无法向父类传递参数(因为传什么参数都会被所有子类实例共享,因此最好不传递任何参数),因此无法对父类中的属性进行实例化 // 这是原型链继承的第一个缺点 Teacher.prototype = new Person(); Teacher.prototype.getSchool = function () { return this.school; } var t1 = new Teacher('BUPT'); var t2 = new Teacher('BUPT'); console.log(t1 instanceof Teacher); console.log(t1 instanceof Person); console.log(t2 instanceof Teacher); console.log(t2 instanceof Person); console.log(Teacher.prototype.isPrototypeOf(t1)); console.log(Person.prototype.isPrototypeOf(t1)); console.log(Teacher.prototype.isPrototypeOf(t2)); console.log(Person.prototype.isPrototypeOf(t2)); console.log(t1.getSchool()); console.log(t1.getName()); console.log(t1.getAge()); console.log(t2.getSchool()); console.log(t2.getName()); console.log(t2.getAge()); t1.school = 'BNU'; // 注意 // 如果采用第一种设置属性的方式,那么在对象t1和其__proto__里面有相同的两份属性,只不过属性值不一样 // 如果采用第二种设置属性的方式,那么很显然,对象t2也将获得与对象t1相同的属性值 // 这是原型链继承的第2个缺点 // 第一种 // t1.name = 'Tom'; // t1.age = 20; // 第二种 t1.__proto__.name = 'Tom'; t1.__proto__.age = 20; console.log(t1.getSchool()); console.log(t1.getName()); console.log(t1.getAge()); console.log(t2.getSchool()); console.log(t2.getName()); console.log(t2.getAge());
3.构造函数式继承
// 构造函数式继承 // 父类 function Person(name,age) { this.name = name; this.age = age; } Person.prototype.getName = function () { return this.name; } Person.prototype.getAge = function () { return this.age; } // 子类 function Teacher(name, age, school) { Person.apply(this, arguments); this.school = school; } Teacher.prototype.getSchool = function () { return this.school; } var t1 = new Teacher('Tom', 20, 'BUPT'); var t2 = new Teacher('Joy', 18, 'BNU'); console.log(t1 instanceof Teacher); console.log(t1 instanceof Person);// false console.log(t2 instanceof Teacher); console.log(t2 instanceof Person);// false console.log(Teacher.prototype.isPrototypeOf(t1)); console.log(Person.prototype.isPrototypeOf(t1)); console.log(Teacher.prototype.isPrototypeOf(t2)); console.log(Person.prototype.isPrototypeOf(t2)); // 注意 // 由于这种构造函数式继承没有涉及到父类的原型,因此子类实例无法访问父类prototype中定义的属性和方法 // 这是构造函数式继承的明显缺点 // console.log(t1.getName()); console.log(t1.name); console.log(t1.getSchool()); // console.log(t2.getName()) console.log(t2.name); console.log(t2.getSchool());
4.组合式继承:原型链继承+构造函数式继承
// 原型链继承+构造函数式继承=组合式继承 // 父类 function Person(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function () { return this.name; } Person.prototype.getAge = function () { return this.age; } // 子类 function Teacher(name, age, school) { // 构造函数式继承 Person.apply(this, arguments); this.school = school; } // 原型链继承 Teacher.prototype = new Person(); Teacher.prototype.getSchool = function () { return this.school; } var t1 = new Teacher('Tom', 20, 'BUPT'); var t2 = new Teacher('Joy', 18, 'BNU'); console.log(t1 instanceof Teacher); console.log(t1 instanceof Person); console.log(t2 instanceof Teacher); console.log(t2 instanceof Person); console.log(Teacher.prototype.isPrototypeOf(t1)); console.log(Person.prototype.isPrototypeOf(t1)); console.log(Teacher.prototype.isPrototypeOf(t2)); console.log(Person.prototype.isPrototypeOf(t2)); console.log(t1.getName()); console.log(t1.name); console.log(t1.getSchool()); console.log(t2.getName()) console.log(t2.name); console.log(t2.getSchool()); // 注意 // 这种组合式继承将父类构造函数执行了(n+1)遍,n为实例化的对象个数。 // 如果n远大于1的话倒是没什么问题,但是如果n比较小(一般情况应该也不会很大吧),比如n=1,即只实例化一次,那么这一次额外的父类构造函数调用(添加原型时的那次调用)将不能忽略。 // 并且,这次构造函数的调用将在子类的原型添加父类的属性,可以通过下面查看__proto__来看到。 // 因此这种继承方式存在两个缺点:多调用一次父类构造函数造成额外开销,继承冗余不纯净。 console.log(t1.__proto__); console.log(t2.__proto__);
5.寄生组合式继承
请移步1封装。