对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
相关推荐
梦诺3 小时前
vue3 keepAlive+记录滚动条
前端·javascript·vue.js
天若有情6734 小时前
开源轻量双语工具|一键批量查询 NPM 包历史下载量,支持按作者批量统计
javascript·npm·github pages·开源工具·netlify·前端开源·npm克隆量
福兮说5 小时前
纯前端把图片压缩到指定体积:canvas.toBlob 配合二分查找
前端·javascript·canvas·图片处理
志尊宝5 小时前
Vue3 零基础每日笔记(018):按键与鼠标修饰符——回车搜索、Ctrl+S 保存这样写
javascript·vue.js·笔记
可乐鸡翅yeah_6 小时前
HLS流媒体首屏起播慢深度优化,从分片、索引、播放器全链路调优
开发语言·javascript·ecmascript·m3u8·m3u8在线
开开心心就好7 小时前
低年级识字练笔顺工具,电脑和安卓端都能用
前端·javascript·网络·安全·scala·erlang·语音识别
杨利杰YJlio7 小时前
ITSK PE 26U5 测试版解读:组件完善、VMD 修复与服务器支持边界
前端·javascript·后端
夏幻灵7 小时前
Vue / React 列表渲染中的 key:为什么没有 key,Checkbox 可能“对号跑错”?
前端·javascript·vue.js
YWL7 小时前
vue-time-axis-plus进阶:双进度条+跨空白续播+播放指针,打造专业级监控回放体验
前端·javascript·vue·时间轴
岁岁种桃花儿7 小时前
Vue组件第六篇:Vue单文件组件
开发语言·前端·javascript·vue.js