原型和原型链学习(一)


---------JavaScript中除了基本类型外的数据类型,都是对象。但是由于其没有 类(class,ES6引入了class,但只是语法糖)的概念,如何将所有对象联系起来就成了一个问题,于是就有了原型和原型链的概念。
----------1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,但是刚开始的Js没有继承机制,更别提像同时期兴盛的C++和Java这样拥有面向对象的概念。在实际的开发过程中,工程师们发现没有 继承机制很难解决一些问题,必须有一种机制能将所有的对象关联起来。Brendan Eich鉴于以上情况,但不想把Js设计得过为复杂,于是引入了new关键词和constructor构造函数来简化对象的设计,引入了prototype函数对象来包含所有实例对象的构造函数的属性和方法,引入了proto和原型链的概念解决继承的问题。

相关名词概念

原型

JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是该对象的原型
JavaScript的函数对象,除了原型 [proto] 之外,还预置了 prototype 属性
当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]。

原型链

当一个对象调用的属性/方法自身不存在时,就会去自己 [proto] 关联的前辈 prototype 对象上去找
如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”

1、字面量方式

当通过字面量方式创建对象时,它的原型就是Object.prototype。虽然我们无法直接访问内置属性[[Prototype]],但我们可以通过Object.getPrototypeOf()或对象的__proto__获取对象的原型。

var obj = {};
Object.getPrototypeOf(obj) === Object.prototype;   // true
obj.__proto__  === Object.prototype;            // true
2、构造函数的调用

通过函数的构造调用(注意,我们不把它叫做构造函数,因为JavaScript中同样没有构造函数的概念,所有的函数都是平等的,只不过用来创建对象时,函数的调用方式不同而已)也是一种常用的创建对象的方式。基于同一个函数创建出来的对象,理应可以共享一些相同的属性或方法,但这些属性或方法如果放在Object.prototype里,那么所有的对象都可以使用它们了,作用域太大,显然不合适。于是,JavaScript在定义一个函数时,同时为这个函数定义了一个 默认的prototype属性,所有共享的属性或方法,都放到这个属性所指向的对象中。由此看出,通过一个函数的构造调用创建的对象,它的原型就是这个函数的prototype指向的对象。

var f = function(name) { this.name = name };
f.prototype.getName = function() { return this.name; }   //在prototype下存放所有对象的共享方法
var obj = new f('JavaScript');
obj.getName();                  // JavaScript
obj.__proto__ === f.prototype;  // true
3、Object.create()

第三种常用的创建对象的方式是使用Object.create()。这个方法会以你传入的对象作为创建出来的对象的原型。

var obj = {};
var obj2 = Object.create(obj);
obj2.__proto__ === obj;       // true
这种方式还可以模拟对象的“继承”行为。

function Foo(name) {
    this.name = name;
}

Foo.prototype.myName = function() {
    return this.name;
};

function Bar(name,label) {
    Foo.call( this, name );   //
    this.label = label;
}

// temp对象的原型是Foo.prototype
var temp = Object.create( Foo.prototype );  

// 通过new Bar() 创建的对象,其原型是temp, 而temp的原型是Foo.prototype,
// 从而两个原型对象Bar.prototype和Foo.prototype 有了"继承"关系
Bar.prototype = temp;

Bar.prototype.myLabel = function() {
    return this.label;
};

先了解 普通对象与函数对象

  1. JavaScript中,万物皆对象,但是对象也是有区别的,Object 和 function 是 JS 自带的函数对象。下面举例:
console.log(typeof Object); //function 
console.log(typeof Function); //function  

//普通对象
var o1 = { };
var o2= new Object( );
var o3 =new f1( );

console.log(typeof o1); //object 
console.log(typeof o2); //object 
console.log(typeof o3); //object


//函数对象
function f1( ){ };   //通过new Function( )创建的对象
var f2 = function(){ };  //通过new Function( )创建的对象
var f3 = new Function('str','console.log(str)');

console.log(typeof f1); //function 
console.log(typeof f2); //function 
console.log(typeof f3); //function   

构造函数

复习一下构造函数怎样生成实例?

function Person(name, age, job) {
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('半个橙子', 28, '新世界的卡密');  // person1是Person 的实例
var person2 = new Person('毕游侠', 30, '炉石');   //实例  // person2是Person 的实例

接着看:每个实例都有一个默认的属性 constructor属性,该属性 (是一个指针) 指向Person

console.log(person1.constructor == Person); //true
 console.log(person2.constructor == Person); //true

即:实例的构造函数属性(constructor)指向构造函数;

原型对象:prototype(原型)

  1. 每个函数对象都有一个prototype属性,这个属性指向函数的原型对象
  2. 记住啊:每个对象都有_ proto _ 属性,但只有函数对象才有prototype属性

还是拿刚刚的构造函数举例子:看这个是构造函数

function Person(name, age, job) {
 this.name = name;
 this.age = age;
 this.job = job;
 this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('半个橙子', 28, '新世界的卡密 呵呵');  // person1是Person 的实例
var person2 = new Person('毕游侠', 30, '炉石');   //实例  // person2是Person 的实例

以下是原型对象:

Person.prototype = {
   name:  '半个橙子',
   age: 28,
   job: '新世界的卡密 呵呵',
   sayName: function() {
     alert(this.name);
   }
}
  1. 其实就是构造函数Person的一个实例,它就是一个普通对象,记住这个原型对象:Person.prototype; 把想象成一个整体A ,A为原型对象; 那么 var A = Person.prototype ;

  2. 例子中还能看到A 添加了四个属性:name age job sayName.其实A还有默认的属性:constructor

  3. 在默认情况下原型对象都会自动获取一个constructor属性,这个属性(是一个指针)指向prototype 属性所在的函数(Person), 代码表示一下就是这个:

A . constructor == Persen;  //Person.prototype原型对象 其实就是个普通对象,也是Person的实例;
Person.prototype.construcor ==  Percon;

上面讲的构造函数说道:实例的构造函数属性(constructor)指向构造函数:

person1.constructor==Person;

那么构造函数通过属性(constructor)的指向 和 原型对象通过属性(constructor)的指向有什么区别呢?它们两个指向都是构造函数(Person); 这说明了什么呢?
说明 原型对象A其实就是个Person的一个实例

原型对象其实就是普通对象(但 Function.prototype 除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。看下面的例子:

function Person(){};  // 构造函数 可以自己试试哦
 console.log(Person.prototype) //Person{}
 console.log(typeof Person.prototype) //Object   类型为对象没错了吧~~
 console.log(typeof Function.prototype) // Function,这个特殊 函数对象???
 console.log(typeof Object.prototype) // Object
 console.log(typeof Function.prototype.prototype) //undefined

Function.prototype 为什么是函数对象呢?

var B = new Function ();
Function.prototype = B;

上文提到凡是通过 new Function( ) 产生的对象都是函数对象。因为 B 是函数对象,所以Function.prototype 是函数对象。


那原型对象是用来做什么的呢?主要作用是用于继承。举个例子:

var Person = function(name){
    this.name = name; // tip: 当函数执行时这个 this 指的是谁?
  };
  Person.prototype.getName = function(){
    return this.name;  // tip: 当函数执行时这个 this 指的是谁?
  }
  var person1 = new person('小M');
  person1.getName(); //小M

从这个例子可以看出,通过给 Person.prototype 设置了一个函数对象的属性,那有 Person 的实例(person1)出来的普通对象就继承了这个属性。具体是怎么实现的继承,就要讲到下面的原型链了。

小问题,上面两个 this 都指向谁?

var person1 = new person('小M');
  person1.name = '小M'; // 此时 person1 已经有 name 这个属性了
  person1.getName(); //小M

故两次 this 在函数执行时都指向 person1。

以上如果有大概了解,请看第二章吧:原型和原型链学习(二)


转自:Yi罐可乐

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值