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复杂数据类型的构造函数都是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
用原型链图表示:
还有一个发现,就是原型链的尽头是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的过程中发生了以下四个步骤:
- 创建一个空对象
- 将空对象的__proto__指向构造函数的prototype
- 执行构造函数,改变this指向为创建出来的空对象
- 如果构造函数有返回值,对构造函数的返回值做判断,返回值为引用类型时,返回该对象,返回值是基本数据类型时是创建出来的空对象。
手写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' }
还没有评论,来说两句吧...