原型
__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对象保存构造函数的prototypethis对象初始化属性和方法- 返回
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()); // errorconsole.log(null.toString()); // error

