原型 prototype
🌈 prototype是 func 的属性,其值是个对象
1 2
| function Handphone() {} console.log(Handphone.prototype);
|
prototype 是构造函数中的,构造出的每个实例对象的公共祖先
所有被该构造函数构造出来的对象,都可以继承原型上的属性和方法
1 2 3 4 5 6 7 8 9 10 11 12
| function Handphone(color, brand) { this.color = color; this.brand = brand; } Handphone.prototype.rom = '64G'; Handphone.prototype.ram = '6G';
var hp1 = new Handphone('red', '小米'); var hp2 = new Handphone('black', 'huawei');
console.log(hp1.rom); console.log(hp2.ram);
|
🌈 constructor 指向构造函数本身
1 2 3 4 5 6
| function Handphone(color, brand) { this.color = color; this.brand = brand; } console.dir(Handphone); console.dir(Handphone.prototype.constructor);
|
constructor 可以被修改
1 2 3 4 5 6 7 8 9
| function Telephone() {} function Handphone(color, brand) { this.color = color; this.brand = brand; } Handphone.prototype = { constructor: Telephone } console.log(Handphone.prototype)
|
🌈 proto 是实例化以后的结果
1 2 3 4 5 6 7 8
| function Car() { var this = { __proto__: Car.prototype } } Car.prototype.name = 'Benz'; var car = new Car(); console.log(car);
|
1 2 3 4 5 6 7 8 9 10 11
| function Person() {} Person.prototype.name = '张三';
var p1 = { name: 'lance' }
var person = new Person(); console.log(person.name); person.__proto__ = p1; console.log(person.name);
|
🌈🌈🌈 实例化对象以后再来修改构造函数的prototype,不影响该对象的原型属性 ,因为修改后的 prototype 指向了新的对象,不影响原来的prototype,但影响原来 prototype 下的 constructor
1 2 3 4 5 6 7 8 9 10 11 12
| Car.prototype.name = 'Benz'; function Car() {} var car = new Car(); Car.prototype = { name: 'Mazda' } console.log(car.name);
console.dir(Car); console.dir(car); console.log(car.__proto__.constructor === Car); console.log(Car.prototype.constructor === Object.prototype.constructor);
|
1 2 3 4 5
| Car.prototype.name = 'Benz'; function Car() {} var car = new Car(); Car.prototype.name = 'hhhh'; console.log(car.name);
|
↑注意是直接替换 **Car.prototype**
还是改 **Car.prototype**
对象下边的属性↑
1 2 3 4 5 6 7 8 9 10 11 12 13
| Car.prototype.name = 'GC'; function Car() {} var car = new Car(); Car.prototype = { name: 'Lance' } console.log(car.name); var car2 = new Car();
console.log(Car); console.log(car); console.log(car2); console.log(car.__proto__.constructor === Car);
|
原型链
沿着原型**__proto__**
往上不断找属性的这条链条叫做原型链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; } var student = new Student();
console.log(student);
student.__proto__.__proto__.__proto__ === Professor.prototype; student.__proto__.__proto__.__proto__.__proto__ === Object.prototype;
|
🌈 原型链的顶端是 Object.prototype
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; } var student = new Student();
console.log(professor, Object.prototype);
|
修改原型上的值
🌈 原型的引用值的属性可以修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; this.success = { alibaba: '28', tencent: '20' } } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; }
var student = new Student(); console.log({ "修改student引用值之前的success": student.success });
student.success.baidu = '100'; console.log({ "修改student引用值之后的student": student });
|
🌈 修改基本值,不影响原型属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; this.success = { alibaba: '28', tencent: '20' } } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; }
var student = new Student();
console.log({ "修改student基本类型之前的success": student.success }); student.success = 666; console.log({ "修改student基本类型之后的student": student });
|
修改一个原型上不存在的引用值,报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; this.success = { alibaba: '28', tencent: '20' } } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; }
var student = new Student();
student.profile.name = 'lance';
|
🌈 原始值自增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Professor.prototype.tSkill = 'Java'; function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.mSkill = 'JS/JQ'; this.students = 500; } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.pSkill = 'HTML/CSS'; } var student = new Student(); console.log(student.students); student.students++; console.log(teacher); console.log(student);
|
分析:学生的students值变了,professor的students值没变。过程是:
student.students++;
=> student.students = student.students + 1
=赋值右边 student 自身没有 students,在原型上读取 students 值,再 + 1 计算
=赋值左边 student.students 是个原始值,则在自身上添加 students 属性
最后赋值
谁调用,this指向谁
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Car() { this.brand = 'Benz'; } Car.prototype = { brand: 'Mazda', intro: function() { console.log('我是' + this.brand + '车'); } } var car = new Car(); car.intro(); console.log(car); Car.prototype.intro();
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| function Person() { this.smoke = function() { this.weight--; } } Person.prototype = { weight: 130 } var person = new Person(); person.smoke(); person.weight; console.log(person); console.log(Person.prototype);
|
🌈 继承
实例化对象继承
- 特点:继承了原型链上的所有属性
- 缺点:继承了一些没必要继承的属性,例如 name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Professor.prototype = { name: 'Mr.Zhang', tSkill: 'Java' } function Professor() {} var professor = new Professor();
Teacher.prototype = professor; function Teacher() { this.name = 'Mr.Wang'; this.mSkill = 'JS'; } var teacher = new Teacher();
Student.prototype = teacher; function Student() { this.name = 'Mr.Li'; this.pSkill = 'HTML'; } var student = new Student(); console.log(student);
|
利用 call/apply 借用构造函数
- 本质:call/apply 是通过改变 this 指向,来借用构造函数的属性和方法
- 此方法实例化的 student 无法继承 Teacher.prototype 原型上的属性
1 2 3 4 5 6 7 8 9 10 11 12
| Teacher.prototype.wife = 'Ms.Liu'; function Teacher(name, mSkill) { this.name = name; this.mSkill = mSkill; } function Student(name, mSkill, age, major) { Teacher.apply(this, [name, mSkill]); this.age = age; this.major = major; } var student = new Student('Mr.B', 'JS', 18, 'Computer'); console.log(student);
|
Student.prototype = Teacher.prototype
- 特点:能够继承 Teacher 原型
- 缺点:Student 和 Teacher 共用原型,修改 Student.prototype 会导致 Teacher.prototype 改变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function Teacher() { this.name = 'Mr.Wang'; this.mSkill = 'JS'; } Teacher.prototype = { pSkill: 'JQ' } var teacher = new Teacher();
Student.prototype = Teacher.prototype; function Student() { this.name = 'Mr.Li'; }
var student = new Student(); Student.prototype.name = 'student'; console.log(student); console.log(Teacher.prototype);
|
🌈 圣杯模式继承(圣杯中间的部位就是连接两头的中间件)
方案:
创建一个空的构造函数
使其继承父类构造函数的原型
由此构造函数实例化出一个对象
把此对象赋值给子类构造函数的 prototype 原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function Teacher() { this.name = 'Mr.Wang'; this.mSkill = 'JS'; } Teacher.prototype = { pSkill: 'JQ' } var teacher = new Teacher();
function Student() { this.name = 'Mr.Li'; }
function Buffer() {} Buffer.prototype = Teacher.prototype; var buffer = new Buffer();
Student.prototype = buffer; Student.prototype.age = 18; var student = new Student(); console.log(teacher); console.log(buffer); console.log(student);
|
封装继承方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Teacher.prototype.name = 'Mr.Wang'; function Teacher() {} function Student() {}
inherit(Student, Teacher); var s = new Student(); var t = new Teacher(); console.log(t); console.log(s);
function inherit(Target, Origin) { function Buffer() {} Buffer.prototype = Origin.prototype; Target.prototype = new Buffer(); Target.prototype.constructor = Target; Target.prototype.super_class = Origin; }
|
圣杯模式+IIFE+闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Teacher.prototype.name = 'Mr.Wang'; function Teacher() {} function Student() {}
inherit(Student, Teacher); var s = new Student(); var t = new Teacher(); console.log(t); console.log(s);
var inherit = (function () { function Buffer() {} return function (Target, Origin) { Buffer.prototype = Origin.prototype; Target.prototype = new Buffer(); Target.prototype.constructor = Target; Target.prototype.super_class = Origin; } })();
|
🌈 圣杯模式 + call/apply 组合继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| function Person(name, age) { this.name = name; this.age = age; this.sayHi = function() { console.log("sayHi"); } }
Person.prototype.eat = function() { console.log("eat"); }
function Student(name, age, score) { Person.call(this, name, age); this.score = score; this.study = function() { console.log("study"); } }
var inherit = (function () { function Buffer() {} return function (Target, Origin) { Buffer.prototype = Origin.prototype; Target.prototype = new Buffer(); Target.prototype.constructor = Target; Target.prototype.super_class = Origin; } })();
inherit(Student, Person); var stu1 = new Student("Lance", 19, 100); console.dir(stu1);
|