目录
1 对象
2 原型
3 原型链
对象
讲到原型链,那么我们要先认识javascript里面的对象,那么什么是对象?
所谓万物皆对象,这句话是不是很抽象,抽象就对了,这是一个很广的概念;对象指的是一类具体的事物;里面封装了属性(property)和方法(method);
例如:
张三是一个对象,
那么张三的属性就有:身高、体重、肤色等等;
而对应的方法(或者是行为)就有:吃饭、睡觉、打豆豆等等
在程序员的世界里,万事万物我们都可以封装成为对象;了解到了对象,那么我们如何在代码中创建一个对象呢?
1 创建对象的三种方式
1.1 字面量形式创建
var hero = {
name: '德玛西亚',
attack: function(){}
}
1.2 new空构造形式创建
var hero = new Object();
hero.name = '德玛西亚';
hero.attack = function(){};
1.3 自定义构造函数
function Hero(name,attack){
this.name = name;
this.attack = attack;
}
var hero = new Hero('德玛西亚',function(){});
1.4 使用构造函数与实例对象的关系
使用构造函数的好处不仅仅在于代码的简洁性,更重要的是我们可以识别对象的具体类型。
在每一个实例对象中同时有一个 constructor
属性,该属性指向创建该实例的构造函数:
var hero1 = new Hero('德玛西亚', function () { });
var hero2 = new Hero('德莱文', function () { });
console.log(hero1.constructor === Hero);//true
console.log(hero2.constructor === Hero);//true
console.log(hero1.constructor === hero2.constructor);//true
- 构造函数是根据具体的事物抽象出来的抽象模板
- 实例对象是根据抽象的构造函数模板得到的具体实例对象
- 每一个实例对象都具有一个
constructor
属性,指向创建该实例的构造函数 - 可以通过实例的
constructor
属性判断实例和构造函数之间的关系
了解到了构造函数与实例对象的关系后,下面我们来看原型
原型
1 为什么会有原型?
我们在使用构造函数的时候,一般都会声明方法,那么就会存在每次实例化一个对象的时候,都会为这些相同的方法创建一块内存区域去存储,这样就会导致内存的浪费;为了解决这个问题,就有了原型 prototype
2 原型与构造函数和示例对象的关系
-
任何函数都具有一个
prototype
属性,该属性是一个对象 -
构造函数的
prototype
对象默认都有一个constructor
属性,指向prototype
对象所在函数 -
通过构造函数得到的实例对象内部会包含一个指向构造函数的
prototype
对象的指针__proto__
-
所有实例都直接或间接继承了原型对象的成员
function Hero() { //定义空的构造函数 } Hero.prototype.sayHi = function() { console.log('你好'); } var hero = new Hero(); hero.sayHi();
上面代码在
Hero
里面没有定义sayHi()
的方法,而是在Hero构造函数的prototype属性(得到的是原型对象)里定义的sayHi()
,在调用的时候让实例对象进行的调用,那么输出结果如下:
原型链
1 什么是原型链?
方法和属性的搜索原则,用白话来讲,就是我们实例化对象在使用属性,或者调用方法的时候,系统是如何帮我们去查找的
2 如何去搜索的
- 搜索首先从对象实例本身开始,例如
hero
实例对象 - 如果在
hero
实例中找到了具有给定名字的属性,则返回该属性的值 - 如果没有找到,则继续搜索
hero
指针hero.__proto__
指向的原型对象,在原型对象中查找具有给定名字的属性 - 如果在原型对象中找到了这个属性,则返回该属性的值,如果还没有找到,那么会从
hero
的原型对象里面的指针hero
原型对象.__proto__
上继续寻找 - 依次类推,一直向上找,当向上找到
Object
的原型的时候,这条原型链就算到头了
实例:
function Hero() {
//定义空的构造函数
}
//给原型对象定义方法
Hero.prototype.sayHi = function() {
console.log('你好');
}
//在原型对象上定义属性
Hero.prototype.name = '我是一个英雄';
var hero = new Hero();
hero.sayHi();
console.log(hero.name);
输出结果如下:
3 注意事项
接着上面例子,不管创建多少个实例对象,如果构造函数里面没有定义属性和方法,是享有的原型对象的,那么它们获取的属性和方法都是同一个
function Hero() {
//定义空的构造函数
}
//给原型对象定义方法
Hero.prototype.sayHi = function() {
console.log('你好');
}
//在原型对象上定义属性
Hero.prototype.name = '我是一个英雄';
//第一个对象
var hero = new Hero();
console.log('我是第一个创建的:' + hero.name);
hero.sayHi();
//第二个对象
var hero1 = new Hero();
console.log('我是第二个创建的:' + hero1.name);
hero1.sayHi();
输出结果
如果获取的属性或者是方法,通过原型链的寻找都没有找到,那么就会出现以下问题
实例代码
function Hero() {
//定义空的构造函数
}
var hero = new Hero();
console.log(hero.name);
hero.sayHi();
输出结果
出现这个结果很好理解,name
属性是我们自己定义的,通过原型链向上搜寻原则,找到Object
的原型对象,里面还没有,那么就是undefined
未定义的;而方法不一样,当我们找到Object
的原型对象,里面没有sayHi()
方法,那么就会抛出异常,sayHi()
不是一个函数;
希望这篇帖子让大家对于原型和原型链的理解有一些帮助;有兴趣小伙伴可以来关注一下我们长沙校区黑马程序员,下面是微信二维码,请加上备注-CSDN