javaScript关于原型链的理解

js原型链

js主要由对象主要分为原型对象、构造函数对象、实例对象。

new原理

使用new可以根据构造函数创建对象,如果构造函数返回非对象值,则创建空对象;反之使用构造函数返回的值

function f1() {
    this.a = "hello"
    return 1
}
function f2() {
    this.a = "hello"
    return {
        b:"world"
    }
}
console.log(new f1());  // {a: "hello"}
console.log(new f2());  // {b: "world"}

原理

new一个新对象的原理:创建空对象,改变该对象prototype,调用构造函数

// instanceof 判断该实例是否属于该对象
function fn() {
    this.a = "123";
}
var p1 = new fn;
console.log(p1);
console.log(p1 instanceof fn);  // 判断p1是否是fn的实例

//等价于
var p2 = new Object();   // 创建一个空对象
p2.__proto__ = fn.prototype;  // 将对象原型指向构造函数的原型
fn.call(p2);  // 将实例对象赋值给构造函数
console.log(p2);
console.log(p2 instanceof fn); // 判断p2是否是fn的实例

constructor构造函数

构造函数对象:构造函数是用来初始化对象用的,构造函数对象有prototype指向原型
创建对象-》使用构造函数初始化对象f1
使用构造函数创建,创建prototype原型=》在原型上添加一个构造函数属性constructor

function f1() {
    this.a = 1;
}
var p = new f1;
console.log(p.constructor);  // f1() {this.a = 1;}
console.log(p.__proto__.constructor);  // f1() {this.a = 1;}
console.log(p.constructor === p.__proto__.constructor);  // true

实例上的constructor是从原型上继承来的,所以实例和原型上的constructor指向一个对象

原型对象介绍

原型对象:创建实例对象时,构造函数先创建原型对象,然后原型对象是每个实例对象的父对象,原型对象上的属性和方法,实例对象都可以访问。

function f1() {
    this.a = 1;
}
var p = new f1;
console.log(p.__proto__);   // 实例通过__proto__访问原型对象
console.log(f1.prototype);  // 构造函数使用prototype访问原型对象

实例访问原型使用__proto__方法,构造函数访问原型使用prototype方法

原型链

通过简单函数我们可以画出他的原型链

function Fn(){
    this.a = "123";
}
var p = new Fn;
console.log(p);  // {a: "123"}
console.log(p.__proto__);

// 从实例对象开始,向下找原型
console.log(p);  // {a: "123"}
console.log(p.__proto__);  // Fn.prototype
console.log(p.__proto__.__proto__);  // Object.prototype
console.log(p.__proto__.__proto__.__proto__);  // null

// 构造函数
console.log(p.constructor);  // Fn  从原型继承过来的
console.log(p.__proto__.constructor);  // Fn
console.log(p.__proto__.__proto__.constructor);  // Object
// null 没有构造函数

// Fn 和 Object
// 构造函数
console.log(p.__proto__.constructor.constructor);  // Function
console.log(p.__proto__.__proto__.constructor.constructor);  // Function
// 将FN,Object看成实例,去找他们的原型
console.log(p.__proto__.constructor.__proto__);  // Function.prototype
console.log(p.__proto__.__proto__.constructor.__proto__);  // Function.prototype
// Function的原型
console.log(p.__proto__.constructor.constructor.prototype);  //  Function.prototype      

// Function.prototype的构造函数
console.log(p.__proto__.constructor.constructor.prototype.constructor);  // constructor 
// 将Function.prototype作为实例去找他的原型
console.log(p.__proto__.constructor.constructor.prototype.__proto__);  // Object.prototype

结论:

  1. 实例向下一直找原型,最终指向Object.prototype,而这个原型指向null。也就是说在Object.prototype上定义的属性和方法任何对象都可以使用
  2. 实例和其原型的构造函数是Fn,Object.prototype的构造函数是Object,而他两和Function.prototype都是Function构造,构造函数最终指向Function
  3. 从Function上找原型找到Function.prototype,而他的原型指向Object.prototype,这最终也正式了所有对象原型最终指向Object.prototype

prototype属性的作用

prototype代表对象的原型,相当于对象的父类。我们可以通过将属性和方法或者另一个对象挂载到该对象上,实现js的继承。注意:prototype上的属性和方法都会成为对象的共有属性

// 在原型上添加属性和方法
var Person = function () {
    this.name = "alex";
};
Person.prototype.age = 18;
Person.prototype.showName = function () {
    console.log(this.name);   // this 指向对象Obj    
};
var p1 = new Person;
var p2 = new Person();   // new对象时,如果没有参数,加不加括号都可以,结果都一样  

console.log(p1);  //Person{name: "alex"}
console.log(p2);  // Person{name: "alex"}
// p1和p1都共享一个age
p2.age = 22;  // 在实例上创建age属性
console.log(p1.age);  // 18  p1实例上没有age属性,原型上有age属性
console.log(p2.age);  // 22  p2实例上有age属性,直接取实例上age属性

// p1和p1都调用原型上的prototype属性
p1.showName();  // alex
p2.showName();  // alex

constructor属性

构造函数的属性和作用,构造函数用来初始化函数,初始化原型对象时,在原型对象上添加constructor用来指向构造函数。
Fn原型访问构造函数:Fn.prototype.constructor
f1实例访问构造函数:f1.constructor
实例上的constructor实际上是从其原型对象上找到的。

var Person = function () {
    this.name = "alex";
};
// Person 就是构造函数
var p = new Person();
console.log(Person.prototype.constructor);    // Person对象
console.log(p.constructor);  // Person对象
console.log(Person.prototype.constructor === p.constructor); // true

案例

对象拥有数组的方法

相当于函数的继承,js中继承函数的方法

// js让对象继承数组  
function Fruit() {
}
// 继承Array数组类  
Fruit.prototype = Array.prototype;  // 修改原型对象,将Fruit的原型指向Array的原型
Fruit.prototype.constructor = Fruit;  // 将构造函数的原型指向Fruit
// 因为修改原型后,原型上的构造函数就改变为Array,即修改构造函数指向Fruit。即使用Fruit来初始化函数
var apple = new Fruit();
apple.push("big","middle","small","tiny");  // 可以使用数组的push方法,将元素加入对象中
// 只有数组对象才有forEach方法,作用是遍历数组
apple.forEach(element => {
    console.log(element);
});

一般继承对象的方法,先设置原型,再修改构造函数的指向。

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页