js – 原型链

作者 : admin 本文共3975个字,预计阅读时间需要10分钟 发布时间: 2023-03-8 共3人阅读

js – 原型链

  • 1. js原型链
  • 2. js原型链的应用
    • 2.1 js原型链继承
    • 2.2 利用js原型判断对象类型
    • 2.3 new的过程中发生了什么 / 手写new

1. js原型链

原型链由三个部分组成:构造函数、实例、原型对象。

构造函数:由大写字母开头的函数。在创建构造过程中,js会自动为它添加prototype属性,值为拥有constructor属性原型对象,constructor属性值为该构造函数。只有函数才有prototye属性。
实例:由构造函数new(实例化)得到。
原型对象:构造函数会在创建的过程中自动创建。

举个例子:

function Person(){} // 构造函数
var person = new Person() // 实例对象

关系图如下:
js – 原型链插图
探讨原型链的过程

我们接触到的js复杂数据类型的构造函数都是function类型,因为复杂数据类型的构造函数都是new Function创建的。
typeof 能检测:
1.基本数据类型(除了null) 、
2.function类型、
3.对象(但是不能区分是什么对象,比如数组和对象分不清)

//函数对象  
function Func1(){};   //函数声明
var Func2 = function(){};  // 函数表达式(右边是匿名函数)
var Func3 = new Function("n1,n2","return n1+n2");  // 函数对象(不推荐)
  
console.log(typeof Func1);  //function  
console.log(typeof Func2);  //function  
console.log(typeof Func3);   //function  
console.log(typeof Object);   //function  
console.log(typeof Array);   //function  
console.log(typeof String);   //function  
console.log(typeof Date);   //function  
console.log(typeof Function);   //function  

A instanceof B 是探讨A的__proto__或是__proto__.proto__或是__proto.proto…是否等于B.prototype,如果是则返回true,否则返回false。

Function instanceof Object //true 
Object instanceof Function // true 
// Function.__proto__.__proto__ === Object.prototype
// Object.__proto__ === Function.prototype

是不是感觉有些奇怪,因为Object是构造函数,继承了Function.prototype,Function也是对象,继承了Object.prototype。本质原因还是因为Function构造函数的构造函数是他本身,所以Function.__proto__也指向Function.prototype
用原型链图表示:
js – 原型链插图(1)
还有一个发现,就是原型链的尽头是null,就是说,当实例对象不停的.__proto__尽头是Object.prototype.proto(null)
js内置对象包含Object、Function、Array、String、Boolean、Number、Date、RexExp。

2. js原型链的应用

2.1 js原型链继承

原型链可以用比较小的内存来实现对象共享。只需在原型对象上添加方法(重写原型对象),子实例都可以引用。

function Person() { } // 构造函数
var person = new Person() // 实例对象

Person.prototype.name = 'name';
Person.prototype.get = () => {
    console.log('get')
};
person.get() // 'get'
console.log(person.name); // 'name'

缺点:实例对原型上的引用类型值进行篡改。

Person.prototype.arr = ['a','b'];
person.arr.push('c');
console.log(Person.prototype.arr); // ['a','b','c']

2.2 利用js原型判断对象类型

typeof A:
只能判断除了null之外的基础数据类型。
复杂数据类型+null都会判断为object,函数会判断为function。

A instanceof B:
表示A是否是B这个构造函数的实例对象,A.proto… === B.prototype则成立。
不能判断基本数据类型。

console.log('111' instanceof String) // false
console.log((new String('111')) instanceof String) // true

A.constructor:
利用原型链和原型,不能判断undefined和null。
利用实例的自动查找原型对象上的constructor属性为构造函数。
‘1’.proto.constructor 为 ƒ String() { [native code] }

console.log('1'.constructor);  // ƒ String() { [native code] }
console.log((function a(){}).constructor); // ƒ Function() { [native code] }
console.log(null/undefined.constructor);  // Uncaught TypeError: Cannot read properties of null (reading 'constructor')

Object.prototype.toString.call ( A )
可以检测所有数据类型。

console.log(Object.prototype.toString.call(true)) // '[object Boolean]'
console.log(Object.prototype.toString.call(undefined)) // '[object Undefined]'
console.log(Object.prototype.toString.call(null)) // '[object Null]'
console.log(Object.prototype.toString.call(Person)) // 自定义函数'[object Function]'

2.3 new的过程中发生了什么 / 手写new

new的过程中发生了什么

先看两段代码帮助理解

// 无return
function Person1(name, age) {
    this.name = name;
    this.age = age;
}
// 有return 
function Person2(name, age) {
    this.name = name;
    this.age = age;
    // 试一试返回基本数据类型和引用数据类型的区别(如return null时候,结果跟Person1一样)
    return { type: 'dog', owner: 'lihua' } 
}

var func = () => { console.log('my name is lihua') }

Person1.prototype.getName = func;
Person2.prototype.getName = func;

var person1 = new Person1('lihua', 18);
var person2 = new Person2('lihua', 18);

//{ name: 'lihua', age: 18 } , () => {..} , {getName: ƒ, constructor: ƒ} 
console.log(person1, person1.getName, person1.__proto__)
//{ type: 'dog', owner: 'lihua' } , undefined , Object.prototype
console.log(person2, person2.getName, person2.__proto__)

所以new的过程中发生了以下四个步骤:

  1. 创建一个空对象
  2. 将空对象的__proto__指向构造函数的prototype
  3. 执行构造函数,改变this指向为创建出来的空对象
  4. 如果构造函数有返回值,对构造函数的返回值做判断,返回值为引用类型时,返回该对象,返回值是基本数据类型时是创建出来的空对象。

手写new

根据以上,手写new如下:

function myNew(constructor, ...args) {
    var result = constructor(...args)
    // return 基本数据类型时
    if (typeof result !== 'object' || result === null) {
        let obj = {}                                        //创建空对象
        obj.__proto__ = constructor.prototype                // 空对象的__proto__指向构造函数的原型
        constructor.apply(obj, args)                         //改变this指向
        return obj
    }
    // return 引用数据类型时
    return result
}

测试一下

function Constructor(a, b) {
    this.a = a;
    this.b = b;
    return a;
}

Constructor.prototype.getName = () => { return 'hihihi' }

var test = myNew(Constructor, 1, 2)
console.log(test, test.getName) // Constructor {a: 1, b: 2} () => { return 'hihihi' }
本站无任何商业行为
个人在线分享 » js – 原型链
E-->