JS原型相关知识

原型

在概念梳理之前,来看js中经典图


image.png
  • function Foo 就是一个方法,比如JavaScript 中内置的 Array、String 等
  • function Object 就是一个 Object
  • function Function 就是 Function
  • 以上都是 function,所以 .proto都是Function.prototype
  • 再次强调,String、Array、Number、Function、Object都是 function
定义

Javascript规定,每一个函数都有一个prototype原型(对象属性),指向另一个对象(原型链上面的)。
prototype(对象属性)的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变(公用)的属性和方法,直接定义在prototype对象属性上。

prototype就是调用构造函数所创建的那个实例对象的原型(proto)。

prototype可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。

作用
1、数据共享,节省空间
  
    在函数作为构造函数时,在构造函数内部用this.method 初始化对象方法会使得每一个实例化出来的对象都有一个相同的但是占用不同内存的方法
function Person(){}
Person.prototype.say = function(){
    console.log('Hi');
}

利用原型可以使得每一个实例对象都可以调用原型上的say方法,而且节省了内存

2、继承
  比如 组合式继承 = 原型继承 + 借用构造函数继承
 function Person(name) {
        this.name = name;
        this.friends = ['Mike','Jack'];
    }
    function Chinese(name,age) {
        Person.call(this,name);
        this.age = age;
    }
    Chinese.prototype = new Person();

在子类构造函数中借用父类构造函数,再通过原型继承父类的原型属性和方法,模拟继承的效果

函数对象和普通对象
 在 JavaScript 中,将对象分为函数对象和普通对象。所谓的函数对象,其实就是 JavaScript 的用函数来模拟的类实现。JavaScript 中的 Object 和 Function 就是典型的函数对象。
function fun1(){};
const fun2 = function(){};
const fun3 = new Function('name','console.log(name)');

const obj1 = {};
const obj2 = new Object();
const obj3 = new fun1();
const obj4 = new new Function();


console.log(typeof Object);//function
console.log(typeof Function);//function
console.log(typeof fun1);//function
console.log(typeof fun2);//function
console.log(typeof fun3);//function
console.log(typeof obj1);//object
console.log(typeof obj2);//object
console.log(typeof obj3);//object
console.log(typeof obj4);//object

上述代码中,obj1,obj2,obj3,obj4都是普通对象,fun1,fun2,fun3 都是 Function 的实例,也就是函数对象。

所有 Function 的实例都是函数对象,其他的均为普通对象,其中包括 Function 实例的实例。

image.png

JavaScript 中万物皆对象,而对象皆出自构造(构造函数)。

上图中,Function 和 new Function 的关系如下:

Function.__proto__ === Function.prototype//true

proto

其中 proto和constructor是对象独有的,prototype属性是函数独有的;

但是在 JavaScript 中,函数也是对象,所以函数也拥有proto和 constructor属性

Object 和 Function 的关系,看一下代码和关系图

 function Person(){…};
 let nealyang = new Person(); 
Object 和 Function 的关系

proto 的例子,说起来比较复杂,可以说是一个历史问题。

ECMAScript 规范描述 prototype 是一个隐式引用,但之前的一些浏览器,已经私自实现了 proto这个属性,使得可以通过 obj.proto 这个显式的属性访问,访问到被定义为隐式属性的 prototype。

因此,情况是这样的,ECMAScript 规范说 prototype 应当是一个隐式引用:

- 通过 Object.getPrototypeOf(obj) 间接访问指定对象的 prototype 对象
- 通过 Object.setPrototypeOf(obj, anotherObj) 间接设置指定对象的 prototype 对象
- 部分浏览器提前开了 __proto__ 的口子,使得可以通过 obj.__proto__ 直接访问原型,通过 obj.__proto__ = anotherObj 直接设置原型
- ECMAScript 2015 规范只好向事实低头,将 __proto__ 属性纳入了规范的一部分
对象a

从浏览器的打印结果我们可以看出,上图对象 a 存在一个proto属性。而事实上,他只是开发者工具方便开发者查看原型的故意渲染出来的一个虚拟节点。虽然我们可以查看,但实则并不存在该对象上。

proto属性既不能被 for in 遍历出来,也不能被 Object.keys(obj) 查找出来

访问对象的 obj.proto 属性,默认走的是 Object.prototype 对象上 proto 属性的 get/set 方法。

Object.defineProperty(Object.prototype,'__proto__',{
    get(){
        console.log('get')
    }
});

({}).__proto__;
console.log((new Object()).__proto__);

image.png

这里需要知道的是,proto是对象所独有的,并且proto是一个对象指向另一个对象,也就是他的原型对象。我们也可以理解为父类对象。它的作用就是当你在访问一个对象属性的时候,如果该对象内部不存在这个属性,那么就回去它的proto属性所指向的对象(父类对象)上查找,如果父类对象依旧不存在这个属性,那么就回去其父类的proto属性所指向的父类的父类上去查找。以此类推,知道找到 null。而这个查找的过程,也就构成了我们常说的原型链。

prototype

规范里,prototype 被定义为:给其它对象提供共享属性的对象。其实prototype 自己也是对象。

所有对象,都可以作为另一个对象的 prototype 来用。

image.png

修改proto的关系图,添加了 prototype,prototype是函数所独有的。它的作用就是包含可以给特定类型的所有实例提供共享的属性和方法。它的含义就是函数的远行对象,也就是这个函数所创建的实例的远行对象,正如上图:nealyang.proto === Person.prototype。 任何函数在创建的时候,都会默认给该函数添加 prototype 属性.

constructor

constructor属性也是对象所独有的,它是一个对象指向一个函数,这个函数就是该对象的构造函数。

注意,每一个对象都有其对应的构造函数,本身或者继承而来。单从constructor这个属性来讲,只有prototype对象才有。每个函数在创建的时候,JavaScript 会同时创建一个该函数对应的prototype对象,而函数创建的对象.proto === 该函数.prototype,该函数.prototype.constructor===该函数本身,故通过函数创建的对象即使自己没有constructor属性,它也能通过proto找到对应的constructor,所以任何对象最终都可以找到其对应的构造函数

唯一特殊的可能就是我开篇抛出来的一个问题。JavaScript 原型的老祖宗:Function。它是它自己的构造函数。所以Function.prototype === Function.__proto。

为了直观了解,我们在上面的图中,继续添加上constructor:

image.png

其中 constructor 属性,虚线表示继承而来的 constructor 属性。

你可能感兴趣的:(JS原型相关知识)