12.1原型
所有的对象都有自己的原型,包括原型本身也有自己的原型
function Car(){}
var car = new Car();
console.log(Car.prototype);
console.log(car);
12.2原型链
沿着proto去找原型里的属性,一层一层的去继承原型的属性的这条链条叫做原型链
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.mSkill; // JS/JQ
student.tSkill; // JAVA
原型链的顶端是 Object.prototype
原型上的引用值可以修改
Professor.prototype.tSkill = 'JAVA';
function Professor(){}
var professor = new Professor();
Teacher.prototype = professor;
function Teacher(){
this.mSkill = 'JS/JQ';
this.success = {
alibaba: '28',
tencent: '30'
}
}
var teacher = new Teacher();
Student.prototype = teacher;
function Student(){
this.pSkill = 'HTML/CSS';
}
var student = new Student();
student.success.baidu = '100';
student.success.alibaba = '29';
console.log(teacher, student);
原型上的原始值不能修改
祖先原型上的原始值不能修改,修改原始值,会认为是给自己添加一个属性值
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();
student.students++;
console.log(student.students);
console.log(teacher, student);
student.students++相当于student.students+1=501,然后将其赋值给student的students
面试题
function Car(){
this.brand = 'Benz';
}
Car.prototype = {
brand: 'Mazda',
intro: function(){
// 这里的 this 指向的是 Car 自己,Car 自己有 brand,所以不回去原型上找了,谁在使用 this 就指向谁
console.log('我是' + this.brand + '车');
}
}
var car = new Car();
car.intro(); // Benz
function Person(){
// this = {
// weight: 129
// }
this.smoke = function(){
this.weight--;
}
}
Person.prototype = {
weight: 130
}
var person = new Person();
person.smoke();
console.log(person.weight);
console.log(Person.prototype);
console.log(Person);
为什么控制台执行函数,函数没有返回值的时候回打印undefined
因为普通函数没有写返回值,默认返回undefined;
构造函数通过实例化以后,返回的是this
自定义的构造函数的构造器指向自定义的构造函数
// 字面量构造
var obj1 = {};
console.log(obj1);// Object顶端原型
// 系统内置对象构造,一般不使用
var obj2 = new Object();
console.log(obj2);// Object顶端原型
// 自定义构造函数
function Obj(){}
var obj3 = new Obj();
console.log(obj3);//自定义的构造函数的构造器指向自定义的构造函数
原型也有原型
function Obj(){}
var obj = new Obj();
console.log(obj.__proto__);
12.3 Object.create(对象 / null) 创建对象
提供了一个自定义原型的功能
function Obj(){}
Obj.prototype.num = 1;
var obj1 = Object.create(Obj.prototype);
var obj2 = new Obj();
console.log(obj1);
console.log(obj2);
// obj1 和 obj2 产生的效果是一模一样的
var test = {
num: 2
}
function Obj(){}
Obj.prototype.num = 1;
// Object.create 提供了一个自定义原型的功能
var obj1 = Object.create(test);
// 将 test 作为原型传递进来
var obj2 = new Obj();
//new做了什么
//实例化obj2
//调用构造函数Obj的初始化属性和方法
//指定实例对象的原型
console.log(obj1);
console.log(obj2);
Object.create 把其它的对象作为原型来使用
// 创建 obj1 空对象
var obj1 = Object.create(null);
console.log(obj1); // {} 里面什么都没有,也没有原型
、
var obj1 = Object.create(null);
console.log(obj1);
obj1.num = 1;
var obj2 = Object.create(obj1);
console.log(obj2); // obj1 作为 obj2 的原型传递进来,所以挂载到了 proto 上
不是所有的对象都继承于 Object.prototype,如 Object.create(null)
var obj = Object.create(null);
obj.num = 1;
console.log(obj); // 没有原型
var obj = Object.create(null);
obj.num = 1;
var obj1 = {
count: 2
}
// 这只是自己自定义的一个属性,而不是原型
// proto 必须得是系统内置的,可以更改它,但不能自造
obj.__proto__ = obj1;
console.log(obj);// 没有原型
console.log(obj.count); // undefined
console.log(obj1);
undefined 和 null 能否使用 toString 方法
不能,因为 undefined 和 null 不能经过包装类转为 对象,它也没有原型,所以无法继承Object.prototype
原始值是没有属性的。
var num = 1;
var obj = {};
var obj2 = Object.create(null);
document.write(num);//1
// document.write 是一定是经过一个隐式转换,转换为 string 的
document.write(obj); // [object Object]: 对象形式的 Object
document.write(obj2); // 报错:cannot convert object to primitie value
// 因为 obj2 里面什么都没有,没有办法继承 Object.prototype,便没有办法使用 toString 方法,所以报错
obj2.toString = function(){
return '你好';
}
console.log(obj2.toString()); // 你好
12.4原型方法的重写
Obeject.prototype 上有 toString 方法,Number.prototype 上也有自己的 toString方法,这就是对原型方法的重写
Object.prototype.toString.call(1); // '[object Number]':对象类型的 Number 构造函数
Object.prototype.toString.call('a'); // '[object String]'
Number.prototype.toString.call(1); // '1'
包装类(系统内置的构造函数)里都有 toString 方法
为什么Number没有继承Object的toString方法,为什么要自己写一个?
因为 Obeject.prototype 的 toString 打印的值是 [object, 构造函数名],不想要的返回值,所以需要重写一个,是原型里重写了toString 这个方法
12.5call / apply 更改 this 指向
function test(){
console.log('a');
}
test(); // 相当于 test.call();系统隐式的加了 call
call
第一个参数是一个对象,将使用前面构造函数的属性和方法
function Car(brand, color){
this.brand = brand;
this.color = color;
//相当于
//newCar.brand = brand;
//newCar.color = color;
}
var newCar = {};
Car.call(newCar, 'Benz', 'red');
console.log(newCar);
apply
和call一样,后面参数用数组
function Car(brand, color){
this.brand = brand;
this.color = color;
}
var newCar = {};
Car.apply(newCar, ['Benz', 'red']);
console.log(newCar);
12.6应用
计算器
function Compute(){
this.plus = function(a, b){
console.log(a + b);
}
this.minus = function(a, b){
console.log(a - b);
}
}
function FullCompute(){
// 通过 apply 借用 Compute 的功能
Compute.apply(this); // this代表我的实例
this.mul = function(a, b){
console.log(a * b);
}
this.div = function(a, b){
console.log(a / b);
}
}
var compute = new FullCompute();
compute.plus(3, 5);
compute.minus(3, 5);
compute.mul(3, 5);
compute.div(3, 5);
题目
年龄为多少岁姓名为XX 买了一辆排量为xx的什么颜色的什么牌子的车
利用 call 和 apply 合并 Car 和 Person 两个构造函数,打印出上面的这句话
function Car(brand, color, displacement) {
this.brand = brand;
this.color = color;
this.displacement = displacement;
this.info = function(){
return '排量为' + this.displacement + '的' + this.color
+ this.brand
}
}
function Person(opt) {
Car.call(this, opt.brand, opt.color, opt.displacement)
this.name = opt.name;
this.age = opt.age;
this.say = function(){
console.log('年龄' + this.age + '岁姓名为' + this.name + '买了一辆' + this.info());
}
}
var p = new Person({
name: '张三',
age: 28,
brand: '奔驰',
color: '红色',
displacement: '3.0'
});
p.say();