javascript学习笔记(16)--原型&原型链

引入

JavaScript的面向对象编程和大多数其他语言如Java、C#的面向对象编程都不太一样

面向对象编程的两个基本概念

  1. 类:类是对象的类型模板,例如,定义Student类来表示学生,类本身是一种类型,Student表示学生类型,但不表示任何具体的某个学生;
  2. 实例:实例是根据类创建的对象,例如,根据Student类可以创建出xiaoming、xiaohong、xiaojun等多个实例, 每个实例表示一个具体的学生,他们全都属于Student类型
    所以,类和实例是大多数面向对象编程语言的基本概念

不过,JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程

什么是原型

原型是指当我们想要创建xiaoming这个具体的学生时,我们并没有一个Student类型可用,但恰好有这么一个现成的对象,我们就根据这个现成的对象创建xiaoming,由于xiaoming继承的是这个现成对象的属性,那么这个现成对象就叫做原型

var Student = { 
name: 'Robot', 
height: 1.2,
 run: function () { console.log(this.name + ' is running...'); } };//这个Student就是我们的原型
var xiaoming = { name: '小明' };
xiaoming.__proto__ = Student; //xiaoming的原型指向了对象Student,看上去xiaoming仿佛是从Student继承下来的

xiaoming.name
"小明"
xiaoming.height
1.2
xiaoming.run()
小明 is running...

xiaoming有自己的name属性(我们自己定义的),但并没有定义run()方法和height,由于小明是从Student继承而来,只要Student有,xiaoming也可以使用

深入理解原型

JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已,如果你把xiaoming的原型指向其他对象

var Bird = {
name:"bird",
 fly: function () { console.log(this.name + ' is flying...'); } };
 xiaoming.__proto__ = Bird; 

xiaoming.run()
TypeError: xiaoming.run is not a function
xiaoming.fly();
小明 is flying...

现在xiaoming已经无法run()了,他已经变成了一只鸟

在JavaScrip代码运行时期,你可以把xiaoming从Student变成Bird,或者变成任何对象
请注意,在编写JavaScript代码时,不要直接用obj.__proto__去改变一个对象的原型,这里只是为了演示方便,不过要记住的是学会用__proto__去查看原型

创建对象

Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象

var Student = { 
 name: 'Robot',
  height: 1.2, 
  run: function () { console.log(this.name + ' is running...');}
   };//原型
var newstudent=Object.create(Student)//新对象

newstudent
{}//没有自己的属性
<prototype>: Object { name: "Robot", height: 1.2, run: run() }
//都是继承属性 
newstudent.name
"Robot"
newstudent.height
1.2
newstudent.run()
Robot is running...
newstudent.hasOwnProperty('run')
false
newstudent.hasOwnProperty('name')
false
newstudent.hasOwnProperty('height')
false

说明新对象什么属性都没有,那么为什么又会正常显示出newstudent.name,height和run()呢?

这不得不讲javascript的机理了
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象,当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象(Object.prototype表示Object 的原型对象),最后,如果还没有找到,就只能返回undefined
所以我们这里虽然newstudent其实是一个空对象,但由于原型的原因,他还是可以正常显示属性,但实际上不是他的属性

既然通过Object.create可以建立原型链,并创建一个空的对象,我们也可以利用这个来创造对象

 var Student = { 
 name: 'Robot',
  height: 1.2, 
  run: function () { console.log(this.name + ' is running...');}
   };
function createStudent(name) { 
var s = Object.create(Student); 
 s.name = name; 
 s.sex="boy";
 return s; 
 }
 var xiaoming = createStudent('小明'); 

xiaoming
Object { name: "小明", sex: "boy" }
xiaoming.name
"小明"
xiaoming.sex
"boy"
xiaoming.height
1.2
xiaoming.run()
小明 is running...
 
 xiaoming.hasOwnProperty('name')
true
xiaoming.hasOwnProperty('height')
false
xiaoming.hasOwnProperty('run')
false
xiaoming.hasOwnProperty('sex')
true

可以看出,利用原型创建函数其实和之前没什么区别,也是类似于键值对的操作,只不过这里是在函数内部,就只能通过.的方式赋值
我们创建的对象实际上拥有的属性就是我们在函数内赋值的属性,而继承部分的属性跟原型保持一致
如果我们赋值的属性和原型的属性冲突了,由于javascript的机制,最后访问的值还是优先我们对象本身

一定要注意,继承的部分不算是我们自己的,利用hasownproperty会返回false
利用刚才学习的__proto__,可以看出xiaoming的原型确实是我们之前的student对象

  xiaoming.__proto__ === Student; // true 

原型链

刚才中间提到了一下javascript查询object.xx属性的机理,那里其实就涉及了原型链的概念
javascript里最大的原型其实就是我们的Object.prototype

Object.prototype.__proto__
null//没有原型

因为javascript里面一切是对象嘛,说明对象的原型就是javascript的基础,其他的数据类型都是由这个展开
比如说
array

var arr = [1, 2, 3];

arr.__proto__
Array []//也就是 Array.prototype 
arr.__proto__.__proto__
Object {};也就是 Object.prototype
arr.__proto__.__proto__.__proto__
null

其原型链是
arr ----> Array.prototype ----> Object.prototype ----> null Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法

function

function foo(){return foo;}

foo.__proto__
function ();也就是 Function.prototype 
foo.__proto__.__proto__
Object {};也就是 Object.prototype
foo.__proto__.__proto__.__proto__
null

函数也是一个对象,它的原型链是
foo ----> Function.prototype ----> Object.prototype ----> null 由于Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法

number

var a=1;

a.__proto__
Number { 0 };//Number.prototype
a.__proto__.__proto__
Object {};// Object.prototype
a.__proto__.__proto__.__proto__
null

数字也是一个对象,它的原型链是
foo ----> Number.prototype ----> Object.prototype ----> null

object

var a={name:"xiaoming"}

a.__proto__
Object {};//Object.prototype
a.__proto__.__proto__
null

它的原型链是
Object ----> Object.prototype ----> null

这个最基本的原型链大家应该也清楚了吧,其中最特殊的就是我们的object,其他的都类似

最后还有一点,大家可以注意一下
在使用hasOwnProperty(‘sex’)判断是不是非继承属性的时候,一定要用单引号引起来,不然判断的是window下的属性(应该是的吧哈哈哈…盲猜选手)

xiaoming.hasOwnProperty('sex')
true
xiaoming.hasOwnProperty(sex)
ReferenceError: sex is not defined

猜你喜欢

转载自blog.csdn.net/azraelxuemo/article/details/106914793