JS基础知识 - 原型及原型链

函数和对象的关系:

函数是对象的一种

Function instanceof Object  // true

但是对象是由函数创建的

// 语法糖写法
var obj = {a:10, b: 20};
var arr = [5, 'x', true];
    
// 对应的内部转换
var obj = new Object();

obj.a = 10;
obj.b = 30
obj    // {a: 10, b: 30}

var arr = new Array();
arr[0] = 5
arr    // [5]

typeof Array    // “function"
typeof Object    // “function"

了解原型:

下面通过一个构造函数的举例子来开始

function Fn(){
      this.mame = 'emoji';
      this.age = 19;
}

// fn1 :实例对象  Fn() : 构造函数
var fn1 = new Fn();
  1. 每一个函数都有一个属性prototype,通过函数名.prototype指向的,就是函数的原型,也叫作函数的显示原型
Fn.prototype
  1. prototype的属性值是一个对象
typeof Fn.prototype    // "object"
  1. prototype默认只有一个constructor属性,指向函数本身,即例子中的Fn
Fn.prototype
▼ {constructor: ƒ}
    ▶ constructor: ƒ Fn()
    ▶ __proto__: Object

> Fn.constructor
< ƒ Function() { [native code] }

var arr = new Array();
Array.prototype.constructor   ==  ƒ Array() { [native code] }
  1. 也可以通过prototype为函数增加属性或修改原有属性,属性的值可以是多种类型值
function Fn(){
    // 基本属性
    this.mame = 'emoji';
    this.age = 19;
}

Fn.prototype.year = 1999;

Fn.prototype.getName = function(){
    return Fn.name;
}

var fn1 = new Fn();

Fn.prototype
▶ {year: 1999, getName: ƒ, constructor: ƒ}
  1. 每一个对象都有一个属性__proto__指向创建该对象的函数的prototype(实例对象的隐式原型等于函数的显示原型)
fn1.__proto__
▶ {year: 1999, getName: ƒ, constructor: ƒ}

fn1.__proto__ === Fn.prototype    // true

基于上面5点,先看下面例子的输出结果,都为true,如果理解上面5点,那么对输出结果就不存在任何异议:

fn1.__proto__.__proto__ === Object.prototype
// true
Fn.prototype.__proto__ === Object.prototype
// true
fn1.__proto__.__proto__ === Fn.prototype.__proto__ 
// true
  1. fn1.__proto__指向创建fn1的函数:Fn的原型Fn.prototype
  2. Fn.prototype返回的是一个对象,对象的__proto__又指向创建该对象的函数:Object的原型Object.prototype

那么按照上面的逻辑,Object.prototype也是一个对象,遵循每一个对象都有属性__proto__Object.prototype肯定也能指向创建它的函数的原型,但是Object.prototype.__proto__指向的是个null

那么Fn也是一个函数Function,他肯定也存在__proto__,那Fn.__proto__的原型是什么呢,函数是由什么创建的呢,函数的原型又是什么呢?

// 函数的隐式原型指向创建它的函数的显示原型
Fn.__proto__    // ƒ () { [native code] }

// 函数由Function创建
Fn.__proto__ === Function.prototype
// true

function Fn(){
    // 基本属性
    this.mame = 'emoji';
    this.age = 19;
}
 
var Fn = new  Function('this.mame = 'emoji';this.age = 19;');

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

判断一个引用类型的详细对象类型:instanceof

function Fn(){
    // 基本属性
    this.mame = 'emoji';
    this.age = 19;
}

Fn.prototype.year = 1999;

Fn.prototype.getName = function(){
    return Fn.name;
}

var fn1 = new Fn();

// instanceof分为两个参数:A - 对象    B - 函数
fn1 instanceof Fn    // true
Fn instanceof Object    // true
typeof fn1    // "object"
typeof Fn    // "function"

Instanceof的判断原则是:沿着A__proto__这条线来找,同时沿着Bprototype这条线来找,如果两条线能找到同一个引用,即同一个对象,那么就返回true。如果找到终点还未重合,则返回false

内部实现:

// L即fn1  R即Fn
function instance_of(L, R) {
   // O为Fn.prototype
   var R = R.prototype; 
   // L为fn1.__proto__
   L = L.__proto__;
   //执行循环
   while (true) {
        // 不通过
        if (L === null)   
            return false; 
        // 判断 L.__proto__ === R.prototype
        if (L === R)
             return true;
        L = L.__proto__;                   
   }
}      

通过以上内部实现分析,不难理解

Object instanceof Function    // true
// 对象的隐式原型指向创建该对象的函数的显示原型,对象由函数创建。函数的显示原型就是函数本身
Object.__proto__ === Function.prototype
Function instanceof Object    // true
Function.__proto__ === Object.prototype
false

// Function.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
true
JS基础知识 - 原型及原型链_第1张图片
原型查找.png

原型链

function Foo(){}
var f1 = new Foo();
    
f1.a = 10;
Foo.prototype.a = 100;
Foo.prototype.b = 200;
    
f1.a    // 10
f1.b    // 200
Foo.prototype
▶ {a: 100, b: 200, constructor: ƒ}
f1.__proto__
▶ {a: 100, b: 200, constructor: ƒ}
  • 为什么fn1可以使用Fn的方法?
    fn是从Fn函数new出来的实例对象,Fn是一个构造函数。(构造函数就像一个面团,实例对象就是用面团捏出来的各种自己想要的样子,他们的共性就是原材料是一样的,存在一种继承关系)这样fn就能调用Fn.prototype中的属性和方法
  • 为什么fn1Fn都定义了a属性,不会相互影响,而且a是10
    因为f1是Foo函数new出来的对象,af1对象的基本属性,b因为f1.__proto__指向Foo.prototype,在访问一个对象的属性时,先从对象的基本属性里找如果没有则沿着对象的隐式原型(即创建该对象的函数的显示原型)上去找,这就是原型链
var F = function() {}
Object.prototype.a = function() {}; 
Function.prototype.b = function() {};

// f能不能访问到a、b方法呢?
var f = new F();

f__proto__指向F.prototypeF.prototype.__proto__指向Object.prototype,所以f可以取到a方法, 由于f的原型链上没经过Function.prototype,所以取不到b方法。

由于构造函数F是由Function new出来的,所以F.__proto__指向Function.prototype,所以F函数可以取到b方法。
具体属性查找分析:

  1. 先在实例对象f的内部属性里找一遍;有就直接用,发现没有想要的属性,跳转第二步;
  2. f__proto__F.prototype)中找一遍;有就直接用,发现还是没有并不会放弃,跳转到第三步;
  3. f__proto__指向的原型F.prototype__proto__中找一遍,F.prototype.__proto__ === Object.prototype,在Object.prototype中找到属性a,寻找结束;
  4. 在对象Object.prototyp中还是找不到,则继续沿着对象Object.prototyp__proto__找,可是前面有说明,Object.prototyp.__proto__返回的是null,就说名访问的属性不存在;

搜索轨迹:f(F) -> f.__proto__(F.prototype) ->F.prototype.__proto__->Object.prototype->Object.prototype.__proto__->null

这种搜索的轨迹,形似一条长链, 又因 prototype在这个搜索规则中充当链接的作用,于是我们把这种实例与原型的链条称作原型链

如何区分一个属性是不是基本属性:hasOwnProperty

基于上面的例子

var item;
for(item in f1){
    if(f1.hasOwnProperty(item)){
        console.log(item);    // a
    }
}

for(item in f1){
    console.log(item);    // a b
}

你可能感兴趣的:(JS基础知识 - 原型及原型链)