对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
相关推荐
西陵2 小时前
Nx带来极致的前端开发体验——使用MF进行增量构建
前端·javascript·架构
JackieDYH3 小时前
vue3中reactive和ref如何使用和区别
前端·javascript·vue.js
伍哥的传说3 小时前
解密 Vue 3 shallowRef:浅层响应式 vs 深度响应式的性能对决
javascript·vue.js·ecmascript·vue3.js·大数据处理·响应式系统·shallowref
前端开发爱好者4 小时前
弃用 html2canvas!快 93 倍的截图神器
前端·javascript·vue.js
ss2734 小时前
手写MyBatis第39弹:深入MyBatis BatchExecutor实现原理与最佳实践
前端·javascript·html
leon_teacher4 小时前
HarmonyOS权限管理应用
android·服务器·前端·javascript·华为·harmonyos
lumi.5 小时前
HarmonyOS image组件深度解析:多场景应用与性能优化指南(2.4详细解析,完整见uniapp官网)
前端·javascript·小程序·uni-app·html·css3
前端无涯5 小时前
uniapp跨平台开发---uni.request返回int数字过长精度丢失
javascript·uni-app
liangshanbo12157 小时前
Speculation Rules API
前端·javascript·html
石国旺7 小时前
前端javascript在线生成excel,word模板-通用场景(免费)
前端·javascript·excel