对js原型和原型链的理解

本文会解释产生原型和原型链的原因、作用、概念和使用方式。

概念

原型对象: 构造函数的prototype属性指向一个对象,该对象是由这个构造函数实例化出来的对象的原型对象。具有公共属性和方法的对象。

对象的原型:每个对象都有一个原型对象,可通过Object.getPrototypeOf()获取。

构造函数的prototype属性:每个函数都有一个prototype属性,指向一个对象,该对象的属性和方法会被构造函数创建的实例对象继承。

实例的__proto__属性:实例对象的__proto__属性指向其构造函数的prototype属性。

原型链:原型对象也有一个原型,以此类推形成原型链。基本思想是让一个引用对象继承另一个引用对象的属性和方法。

由来

产生原型和原型链是为了解决什么问题?

每个原始数据类型和对象都有对应的方法和属性,当我们反复实例一个对象时,每个实例对象都会对应各自的方法和属性,都是单独开辟空间进行存储的。而他们的方法和属性相同却不互通的。存储这些相同的方法和属性是很占用空间的。因此需要原型将这些相同的、共有的属性和方法单独存放起来。堪比垃圾回收机制中,可能有多个栈内存空间同时指向同一个堆内存空间,但不会出现一个栈内存空间同时指向多个堆内存空间。

垃圾回收机制(GC)

原始数据类型和对象的引用地址是在栈内存空间开辟一个空间来存储的。

对象引用地址的数据是在堆内存空间开辟一个空间进行存储。

二者由栈内存空间指向堆内存空间,两者一一对应。不会出现不同的栈内存空间指向同一个堆内存空间。若栈内存没有指向堆内存空间(没被标记),则堆内存空间的数据会判定为不用的数据,从而被清除。

作用 :是自动清理那些不再使用的内存,防止出现内存泄漏。核心是标记清除和引用计数。

标记清除:从根对象(全局对象)出发标记对象,清除未标记的内存。可解决循环引用,但执行时会短暂卡顿

引用计数:记录每个对象的引用次数,归零时回收。但不能处理循环引用,可能导致内存泄漏

V8引擎优化:分代回收和增量回收

注意:避免意外全局变量、及时清除定时器和监听器、谨慎使用闭包引用对象、清除无用的DOM引用

作用

为函数提供一个声明属性和方法的公共区域,使得这个函数的实例对象都能访问,从而减少内存消耗。 1.数据共享 当访问一个实例对象的属性和方法时,会先查找该实例对象本身是否具有该属性和方法,没有则沿着原型链向上查找,直到原型链顶端null,实现代码的复用。而且不同对象共享一个方法时,由于this指向不同,方法的行为也会不同。

2.实现继承

通过设置对象的原型,让一个对象可继承另一个对象的属性和方法。使得可以创建相同行为的对象,而无需定义相同的属性和方法。

3.节约内存

使用情况

new关键字的步骤

1.创建一个新对象

2.将构造函数的this指向这个新对象

3.将新对象的__proto__属性指向构造函数的prototype属性,即构造函数的原型对象

4.执行构造函数

5.返回this

js 复制代码
function Person(name) {
    this.name = name;
}
Person.prototype.eat = function () {
    console.log(this.name+"吃东西");
};
Person.prototype.sleep = function () {
    console.log(this.name+"睡觉");
}
var p1 = new Person("小明");
p1.eat();//小明吃东西
p1.sleep();//小明睡觉
var p2 = new Person("小华");
p2.eat();//小华吃东西
p2.sleep();//小华睡觉
console.dir(p1);
console.dir(p2);
console.dir(Person);
console.dir(p1);
console.log(typeof p1.__proto__);//object
console.log(typeof Person.prototype);//object
console.log(p1.__proto__ === Person.prototype);//true

eat()和sleep()被添加到Person()构造函数的原型prototype上,实例对象在自己的__proto__属性中找到并调用该公共方法和属性。

js 复制代码
 /*  
          1. 新对象都有一个属性 叫__proto__
          2. 构造函数都有一个属性 叫prototype
          3. 构造函数的原型对象上有一个属性 叫constructor 指向构造函数
          4. 所有的原型对象都是Object函数对象构造出来的(除Object本身的原型对象之外)
       */
        var person1 = new Person();
        console.log(person1.__proto__ === Person.prototype); // true
        console.log(Person.prototype.constructor === Person); // true
        console.log(Person.prototype.__proto__ === Object.prototype); // true
        console.log(Object.prototype.__proto__); // null

        console.log(person1.__proto__.__proto__ === Object.prototype); // true
        console.log(person1.__proto__.__proto__.__proto__ === null); // true

        // console.log(person1.toString());
        console.log(Person.prototype.toString);
        console.log(Person.prototype);
        console.log(Object.prototype);

        // 5. 直接量对象 都是Object函数对象构造出来的
        var obj = {};
        console.log(obj); // obj.__proto__  === Object.prototype
		
        function Person() {
            this.name = 'qwer';
        }
        // Person是一个函数 Person是一个对象
        Person.ss = '新增的ss属性';
        console.dir(Person);
        console.dir(Person.ss);

        var per1 = new Person();
        console.log(per1);

        // 1. 所有的函数都是Function函数对象构造的
        console.log(Person.__proto__ === Function.prototype); // true
        console.log(Function.prototype.__proto__ === Object.prototype); // true
        console.log(Function.prototype.__proto__.__proto__ === null); // true
        console.log(Person.__proto__.__proto__.__proto__ === null); // true

        // 2. Function顶层没有人构造 自己构造了自己
        console.log(Function.__proto__ === Function.prototype); // true
        // console.log(Function.prototype.__proto__ === Object.prototype);

        console.log(Function.__proto__.__proto__ === Object.prototype); // true

        console.log(Object.__proto__ === Function.prototype); // true
        // console.log(Function.prototype.__proto__ === Object.prototype);
        console.log(Object.__proto__.__proto__ === Object.prototype); // true
        console.log(Object.__proto__.__proto__.__proto__); // null
        console.log(Function.__proto__.__proto__.__proto__); // null
相关推荐
kyriewen6 小时前
写组件文档写到吐?我用AI自动生成Storybook,同事以后直接抄
前端·javascript·面试
五点六六六7 小时前
你敢信这是非Native页面写出来的渐变效果吗🌝(底层原理解析
前端·javascript·面试
吃西瓜的年年8 小时前
TypeScript
javascript·ubuntu·typescript
熊猫_豆豆11 小时前
一个模拟四轴飞行器在随机气流扰动下悬停飞行的交互式3D仿真网页,包含飞行器建模与PID控制算法
javascript·3d·html·四轴无人机模拟飞行
来恩100312 小时前
jQuery选择器
前端·javascript·jquery
前端繁华如梦12 小时前
树上挂苹果还是挂玻璃球?Three.js 程序化果实的完整实现指南
前端·javascript
CDwenhuohuo12 小时前
优惠券组件直接用 uview plus
前端·javascript·vue.js
川冰ICE13 小时前
TypeScript装饰器与元编程实战
前端·javascript·typescript
AI砖家13 小时前
Vue3组件传参大全,各种传参方式的对比
前端·javascript·vue.js
希望永不加班13 小时前
var局部变量类型推断的利弊
java·服务器·前端·javascript·html