原型
__proto__
保存的「原型」指向function.prototype
function Car() {}
var car = new Car();
console.log(Car.prototype);
console.log(car);
结合上面的图我们能看到car.__protot__
的原型是Car.prototype
,Car.prototype.__proto__
的原型是Object.prototype
所以:所有的对象都有自己的原型,包括原型本身!
原型链
其实上面👆 的情况就是「原型链」。
「原型链」就是去「原型对象」里一层一层寻找相应的属性的这样的继承属性链就是「原型链」(没有的属性先到我自己的实例上寻找,实例上找不到就去原型对象上寻找,如果原型对象上也没有就继续到原型对象的原型对象去寻找)。
function Professor() {}
Professor.prototype.tSkill = "JAVA";
var professor = new Professor();
function Teacher() {
this.mSkill = "JS/JQ";
}
Teacher.prototype = professor;
var teacher = new Teacher();
function Student() {
this.pSkill = "HTML/CSS";
}
Student.prototype = teacher;
var student = new Student();
console.log(student);
console.log(student.mSkill);
console.log(student.tSkill);
student
实例对象是完全可以访问到mSkill
和tSkill
的,他会沿着原型链条一直寻找,直到顶端。
原型链的顶端
原型链的顶端是Object.prototype
function Professor(){}
console.log(Professor.prototype);
原型的原型是由系统自带的构造函数function Object(){}
构造出来的。
实例对象操作原型链上的属性
function Professor() {}
Professor.prototype.tSkill = "JAVA";
var professor = new Professor();
function Teacher() {
this.mSkill = "JS/JQ";
this.students = 500;
this.success = {
alibaba: 28,
tencent: 30,
};
}
Teacher.prototype = professor;
var teacher = new Teacher();
function Student() {
this.pSkill = "HTML/CSS";
}
Student.prototype = teacher;
var student = new Student();
现在先尝试去修改Teacher
下的success
对象
student.success.baidu = 100;
console.log(student, teacher);
现在我们能看到student
实例对象是完全可以更改原型链上引用数据的。
那我们再来看更改一下原始数据:
student.students++;
console.log(student, teacher);
// 当执行到 student.students++ 时,
// 实例对象发现自己没有 students 属性就会到原型上找
// 然后就会执行 this.student = teacher.students++
我们可以看到当用实例对象student
去更改原型上的原始数据,students
属性却新增到实例对象本身上!!!
由此总结:
- 实例对象更改原型链上的「引用数值」会更改原型链上的属性
- 实例对象更改原型链上的「原始数值」会新增到自己的实例上
this 指向
当构造函数和构造函数原型上有相同的属性时,谁使用this
访问,this
就指向谁!!!
function Car() {
this.brand = "Benz";
}
Car.prototype = {
brand: "Mazsa",
intor: function () {
console.log("我是" + this.brand + "车");
},
};
var car = new Car();
// car 实例对象访问 intor() 函数
car.intor(); // 我是Benz车
// prototype 访问 intor() 函数
car.prototype.intor(); // 我是Mazsa车
Object.create()
:::info
Object
构造函数静态方法。
:::
实例化对象的另外一种写法就是使用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);
从外面来看使用new
和Object.create()
创建出来的对象没有任何的区别。
**Object.create()**
的好处只是可以给一个对象自定义原型。
指定原型是一个对象:
var test = {
num: 2,
};
var obj3 = Object.create(test);
console.log(obj3);
指定原型是null
:
var obj1 = Object.create(null);
console.log(obj1);
obj1.num = 1;
var obj2 = Object.create(obj1);
console.log(obj2);
console.log(obj2.num);
当一个对象的原型被指定为**null**
的时候,该对象无法调用任何的方法。
var obj = Object.create(null);
obj.num = 1;
obj.toString(); // error,not a function
这是因为**obj**
被创建的时候原型指定为**null**
,所以**obj**
就没有原型,所以**obj**
是无法根据原型链的规则往上寻找到**Object.prototype**
的,因此也就无法使用任何的方法。
还因为obj
没有原型,所以给它赋值obj.__proto__
是无法使用的。
var obj = Object.create(null);
var obj1 = { count: 1 };
obj.__proto__ = obj1; // 这里的赋值相当于赋值了一个普通的属性
console.log(obj.count); // 所以无法直接访问 count
new 的过程
- 创建
this
对象 this
对象保存构造函数的prototype
this
对象初始化属性和方法- 返回
this
对象
包装类的方法
var num = new Number(1);
console.log(num.toString()); // "1"
var bool = new Boolean("true");
console.log(bool.toString())
因为包装类也是实例化对象,实例化对象是可以访问到prototype
的,所以包装类是完全可以调用自身的方法!!!
而undefind
和null
是无法通过包装类进行包装的,所以它两也就无法调用方法!!!
console.log(undefined.toString()); // error
console.log(null.toString()); // error