原型和原型链

本文详细介绍了JavaScript中的构造函数和原型的概念,以及它们在对象创建和方法共享中的作用。通过示例展示了如何使用构造函数创建对象,以及如何通过原型实现方法的优化,避免内存浪费。同时,解释了原型链的工作原理,包括__proto__属性如何帮助对象查找和访问属性,以及原型链在查找过程中的层次结构。最后,强调了原型链在访问未定义方法时的关键作用,并指出null是原型链的终点。
摘要由CSDN通过智能技术生成

说到原型呀,我们就不得不来了解一下 构造函数

构造函数

是一个专门用来创建对象的函数,当需要创建的对象格式一样时,我们就可以使用构造函数来创建。如下:

function User(name, age) {
    this.name = name;
    this.age = age;
};
const user1 = new User('Jack', 21);
const user2 = new User('Tom', 18);

这样是不是比我们平时用对象字面量来创建更加方便呢?
我们接着往下看:

function User(name, age) {
    this.name = name;
    this.age = age;
    this.sayHello = function () {
        console.log(`我叫${this.name}, 今年${this.age}`);
    }
};
const user1 = new User('Jack', 21);
console.log(user1.sayHello()); // 我是Jack, 今年21岁。
const user2 = new User('Tom', 18);
console.log(user2.sayHello()); // 我是Tom, 今年18岁。

当创建的实例对象,有一个共同的方法时,如果像上面的例子一样,把 sayHello 这个方法保存在不同的存储空间中,就会对性能不好,所以我们就需要对它进行优化:

function User(name, age) {
    this.name = name;
    this.age = age;
};
User.prototype.sayHello = function() {
    console.log(`我叫${this.name}, 今年${this.age}`);
};
const user1 = new User('Jack', 21);
console.log(user1.sayHello()); // 我是Jack, 今年21岁。
const user2 = new User('Tom', 18);
console.log(user2.sayHello()); // 我是Tom, 今年18岁。
console.log(user1.sayHello === user2.sayHello); // true

这个时候,user1user2sayHellow 函数用的就是同一个了。

而且,当我们直接输出 user1 或者 user2 查看对象的时候,是看不到 sayHello 这个函数的,那我们怎么可以调用呢?这就得说说原型了,也就是我们今天的主角。

使用构造函数的注意事项:

  1. 为了便于分清楚普通函数和构造函数,我们一般把构造函数的首字母大写,也就是大驼峰命名!
  2. 构造函数如果不 new 的话,和普通函数没有任何区别!
  3. 当你 new 一个构造函数的时候,构造函数内部会生成一个空对象,并且 this 会自动指向这个空对象,最后会自动返回 this 指向的这个对象。如果手动 return 一个原始值的话,构造函数会把这个 return 忽略,(还是返回 this 指向的那个对象);如果是 return 一个引用值的话,那么就会把这个引用值当作返回对象,忽略 this 指向的那个对象。

原型

原型,其实就是一个普通的对象,每一个函数对象,都有一个原型(prototype),现在我们就来看看原型长什么样吧,我们通过 __proto__ 来访问原型:

function User() {};
const user = new User();
console.log(user.__proto__); // {constructor: f};

我们可以看到,user 的原型是一个对象,对象中有一个 constructor 的属性,我们把它叫做构造器,它的值一个函数,我们再来看看 constructor 的值是什么吧:

// 接着上个代码块
console.log(user.__proto__.constructor); // function User() {};

从上面的输出,我们可以看到 user.__proto__.constructor 指向的是构造出 user 这个实例对象的构造函数 User。就如下图所表示的原因:

[这里有张图片]

这样看起来是不是就很清晰了呢?

那么我们回过头来看看刚刚的之前的那个示例,代码如下:

function User(name, age) {
    this.name = name;
    this.age = age;
}
User.prototype.sayHello = function() {
    console.log(`我叫${this.name}, 今年${this.age}`);
};
const user1 = new User('Jack', 21);
const user2 = new User('Tom', 18);
console.log(user1.__proto__); // {constructor: f};
console.log(user1.__proto__ === User.prototype); // true
console.log(user1.__proto__.constructor === User); // true
// 还记得下面的这个代码吗
user1.sayHello(); // 我是Jack, 今年21岁。

user1 上根本就没有 sayHello 这个方法呀,那么它是怎么找到这个函数并且成功执行的呢?

这个可全要归功于 __proto__ ,这个大功臣呀,让我来简单的介绍一下。

proto

每个对象身上都有一个属性,那就是 __proto__ 了,它是用来指向构造出它的构造函数的原型对象的,相信刚刚上面的示例代码,大家也可以看出来吧。它还有个功能,让我们来看看下面的代码吧:

function A() {};
A.prototype.name = 'aaaa';
const a = new A();

function B() {};
B.prototype = a;
const b = new B();
    
function C() {};
C.prototype = b;
const c = new C();
console.log(c.name); // aaaa

我们明明没有给 c 添加 name 属性,为什么可以输出呢?

这是因为,当 c 想要访问 name 的值时,它会现在自身查找,自身没有时,它会沿着隐式原型 __proto__ 去找 C.prototype,看它的身上有没有,如果没有的话,他就会继续沿着 __proto__ 去找 B.prototype,看它的身上有没有,如果没有的话,他就会继续沿着 __proto__ 去找 A.prototype,然后看到 A.prototype 上有 name 这个属性,那么就会返回 name 的属性值。如果还没有找到,它就会继续沿着隐式原型 __proto__ 去找 Object.prototype,看它的身上有没有,如果没有的话,他就会沿着 __proto__ 去找,然后返回 null。这样一层一层靠着 __proto__ 形成的链式结构,我们就把它叫做原型链。

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

null 是原型链的顶端!

所以有些时候,我们明明没有创建一些方法,却可以使用,就是要归功于原型链!

好了,今天的原型和原型链就到这里了,下次会整理一份更详细的笔记给大家~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值