美文网首页
前端的那些事(六):原型与原型链(精选篇)

前端的那些事(六):原型与原型链(精选篇)

作者: 沐雨芝录 | 来源:发表于2019-04-30 17:42 被阅读0次

前言

首先广为所知的“万物皆对象”是错误的。
简单基本类型string,number,boolean,null,undefined)不是对象。
复杂基本类型object)才是对象;但是object有九个内置对象(String ,Number, Boolean,Object,Function, Array,Date, RegExp,Error

举个例子:

var num1 = 123;
typeof num1; // "Number"
num1 instanceof Number; // false

var num2 = new Number(123);
typeof num2; // "object"
num2 instanceof Number; // true

其实num1这种字面量形式,获取长度,访问值其实是不生效的。但是js引擎自动帮我们转成Number这种形式了。如num1.length

我们这里简单介绍了,不做深入探讨,毕竟js引擎帮我们做了,记住一句话,Object是所有对象的基类

接下来用最通俗易懂的方式给大家解释各种学术名称的原理:
一、__proto__(指针)

Firefox和Chrome 提供__proto__访问器,ECMA标准是[[Prototype]],它是对象内置属性无法访问,可以通过Object.getPrototypeOf()标准方法访问该属性。

从最简单代码开始:

var obj = {};
console.log(obj);

打印的样子:

image.png
只要是对象都有__proto__属性,最底层的constructorObject,也印证了我那句Object是所有对象的基类
二、prototype(原型)

定义

其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。

__proto__区别:

1:每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性。
2: __proto__指向的是当前对象的原型对象,而prototype指向的,是以当前函数作为构造函数构造出来的对象的原型对象。

function Foo() {
  var name = '小米';
}
Foo.prototype.sayHello = function() {
  console.log('hello 靓仔');
};
console.log(Foo.prototype);
image.png
大家可以看到Foo.prototype有了sayHello的函数,自然可以调用。这里有两个问题?
  1. sayHello方法想获取Foo函数中的name怎么办?
  2. 图中constructor的prototype里面也有sayHello函数是怎么回事?
2.1 解答问题1

这就是我们为何使用new进行构造函数的原因,因为它有共用属性,函数共使用,还能继承。

function Foo() {
  this.name = '小明';
}
Foo.prototype.sayHello = function() {
  console.log('hello' + this.name);
};

// var obj = {};
// obj.__proto__ = Foo.prototype;
// Foo.call(obj);
// var f = obj;
var f = new Foo();
console.log(f.sayHello());

这里解释两件事情:(大厂面试必问)

  • new是什么,就是我注释掉的代码。
  • 为何用prototype创建函数sayHello,为了避免每一次new Foo()就创建一次sayHello,占用内存,消耗性能。
2.2 解答问题2

直接引出constructor。

三、constructor(构造函数)

function Foo() {
  // ...
}
console.log(Foo.prototype.constructor === Foo); // true

从中可以看出constructor是函数Foo的prototype内置属性,指向函数本身。就是这么简单。

继续看代码:

function Foo() {
  // ...
}
var a = new Foo();
console.log(a.constructor === Foo); // true

是不是说明构造函数a的constructor指向Foo,答案是否定的。

继续改代码

function Foo() {
  // ...
}
Foo.prototype.constructor = {}
var a = new Foo();
console.log(a.constructor === Foo); // false

原因很简单,就是Foo.prototype的constructor被我们设置为{}了,也就是说a.constructor是{}了,说明这个constructor不安全,在某些情况,我们得让它恢复,所以你会看到如下代码:

function Person() {}
Person.prototype = {
  constructor: Person, //这里指向Person,就不会丢失了
  name: '欣雨',
  age: 19,
  sayName: function() {
    console.log(this.name + '年龄' + this.age);
  },
};
// console.log(Person.prototype);
var p = new Person();
p.sayName(); // 欣雨年龄19

constructor介绍完了,我们来解释一下2.2的问题吧,上述注释代码的console.log(Person.prototype)打开,打印一下看看:

image.png

只要你通过原型prototype创建属性和方法时,那么Person.prototypeconstructor下就会有prototype属性包含你创建的。(这里不是很重要,知道就好)

就是我在介绍2prototype(原型)所说:__proto__指向的是当前对象的原型对象,而prototype指向的,是以当前函数作为构造函数构造出来的对象的原型对象。

四、原型链

定义:

原型链就是通过proto属性从当前对象开始查找它的原型对象(通过prototype生成的),一直查找到null为止。

function Person() {}
Person.prototype = {
  constructor: Person, //这里指向Person,就不会丢失了
  name: '欣雨',
  age: 19,
  sayName: function() {
    console.log(this.name + '年龄' + this.age);
  },
};
var p = new Person();
console.log(p);

打印的p下面就是空对象,而需要的属性在proto下面。

类型:
默认的原型链结构就是:当前对象 -> 构造函数.prototype -> Object.prototype -> null
继承改变默认原型链结构,其实通过_proto查找即可。

五、函数的构造函数Function

普通函数:

function foo(num) {
  console.log(num);
}
foo(2);  // 2

构造函数:

var foo = new Function('num', 'console.log( num )');
foo(2);  // 2

好处就是:
Function是使用字符串构建函数,那么就可以在程序运行过程中构建函数。
以前的函数必须一开始就写好,再经过预解析,一步一步的运行。

写一些底层代码,为了性能可以使用。

六 instanceof 原理

A instanceof B 的原理?(面试常考题)

查看对象B的prototype指向的对象是否在对象A的[[prototype]]链上

A.__proto__ === B.prototype

相关文章

网友评论

      本文标题:前端的那些事(六):原型与原型链(精选篇)

      本文链接:https://www.haomeiwen.com/subject/zdcvnqtx.html