原型
原型是什么
在JavaScript中,函数是一个包含属性和方法的Function类型的对象。而原型(Prototype)就是Function类型对象的一个属性。
在函数定义时就包含了prototype属性,它的初始值是一个空对象。在JavaScript中并没有定义函数的原型类型,所以原型可以是任何类型。
原型是用于保存对象的共享属性和方法的,原型的属性和方法并不会影响函数本身的属性和方法。
//Function类型的属性 -> 所有函数都具有的属性
console.log(Function.prototype);//[Function]
//定义函数
function fn() {
console.log('this is a function');
}
//原型的默认值是空对象
console.log(fn.prototype);//fn {}
//函数包含构造函数 -> 所有引用类型其实都是构造函数
console.log(Number.prototype);//[Number: 0]
console.log(Object.prototype);//{}
//为什么Object.prototype出来的是{},可以使用学过的东西进行验证,
//随机获取一个Object的属性的属性描述符
var result = Object.getOwnPropertyDescriptor(Object.prototype,'constructor');
console.log(result);//因为enumerable: false,不可遍历
效果:
获取原型
通过如下两种方式可以获取对象的原型,从而设置共享的属性和方法:
-
通过构造函数的prototype属性。
function Person(){ console.log('Person instantiated'); } console.log(Person.prototype);
-
通过Object对象的getPrototype(obj)方法。
function Person(){ console.log('Person instantiated'); } console.log(Object.getPrototypeOf(Person));
代码:
function fn() { console.log('this is a function'); } //使用访问对象的属性语法结构 console.log(fn.prototype);//fn {} console.log(fn['prototype']);//fn {} //Object类型提供getPrototypeOf()方法 console.log(Object.getPrototypeOf(fn));//[Function] 这是运行环境的问题,在浏览器就不一样
效果:
原型的属性和方法
通过如下两种方式可以设置原型的属性和方法:
-
原型的属性和方法单独进行定义。
构造函数.prototype.属性名 = 属性值; 构造函数.prototype.方法名 = function(){}
-
直接为原型定义一个新对象
构造函数.prototype = { 属性名 : 属性值, 方法名 : function(){} }
代码:
function fn() {
console.log('this is a function');
}
//变量proto也是一个空对象
// var proto = fn.prototype;
// 新增属性或方法
// proto.name = '肖德硕';
fn.prototype.name = '肖德硕';
console.log(fn.prototype);
// defineProperty()
Object.defineProperty(fn.prototype,'age',{
value : 18,
enumerable : true
});
console.log(fn.prototype);
效果:
构造函数的原型
//定义构造函数
function Hero(){
this.name = '肖德硕';
this.sayMe= function () {
console.log('this is a function');
}
}
//操作构造函数Hero的原型
Hero.prototype.age = 18;
//利用构造函数Hero来创建对象
var hero = new Hero();
console.log(hero);//Hero { name: '肖德硕', sayMe: [Function] }
//为构造函数的原型新增的属性 -> 构造函数创建对象中依旧可以访问
console.log(hero.age);//18
//对象hero中不存在age属性
var result = Object.getOwnPropertyDescriptor(hero,'age');
console.log(result);//undefined
效果:
自有属性与原型属性
- 自有属性:通过对象的引用添加的属性。其他对象可能无此属性;即使有,也是彼此独立的属性
- 原型属性:从原型对象中继承来的属性,一旦原型属性对象中属性值改变,所有继承自该原型的对象属性均改变。
//定义构造函数
function Hero(name){
//构造函数本身的属性 -> 自有属性
this.name = name;
this.sayMe= function () {
console.log('this is a function');
}
}
//通过构造函数Hero的prototype新增属性或方法
//通过原型所定义的属性 -> 原型属性
Hero.prototype.age = 18;
/*
通过构造函数Hero创建对象时
不仅具有构造函数的自有属性
还具有构造函数的原型属性
* */
var hero = new Hero('肖德硕');
console.log(hero.name);
console.log(hero.age);
var hero2 = new Hero('小肖');
console.log(hero2.name);
console.log(hero2.age);
//为对象hero新增age属性
// hero.age = 80;
// console.log(hero.age);//80
//
// console.log(hero);//Hero { name: '肖德硕', sayMe: [Function], age: 80 }
//
// console.log(hero2.age);//18
Hero.prototype.age = 80;
console.log(hero.age);
console.log(hero2.age);
效果:
自有属性与原型属性的优先级
自有属性与原型属性同名时,默认访问的是自有属性 -> 自有属性的优先级高于原型属性
代码:
//定义构造函数
function Hero(){
this.name = '肖德硕'
}
//构造函数的原型
Hero.prototype.name = '小肖';
//构造函数创建对象
var hero = new Hero();
//自有属性与原型属性同名时,默认访问的是自有属性 -> 自有属性的优先级高于原型属性
console.log(hero.name);//肖德硕
//删除对象的属性
delete hero.name;
console.log(hero.name);//小肖
效果
检测自有或原型属性
-
使用hasOwnProperty()方法检测对象是否具有指定的自有属性
function Hero(){} var hero = new Hero(); console.log(hero.hasOwnPrototype("name"));
-
使用in关键字检测对象及其原型链中是否具有指定属性:
function Hero(){} var hero = new Hero(); console.log("name" in hero);
代码:
function Hero() {
this.name = '肖德硕'
}
Hero.prototype.name = '小肖';
var hero = new Hero();
/*
Object.hasOwnProperty(prop)方法
作用 - 判断当前指定属性是否为自有属性
参数
prop - 表示指定属性名称
返回值 - 布尔值
true - 表示存在指定的自有属性
false - 表示不存在指定的自有属性
* */
console.log(Object.hasOwnProperty('name'));//true
/*
使用in关键字检测对象的属性
作用 - 判断对象中是否存在指定属性(自有属性或原型属性)
返回值 - 布尔值
true - 表示存在指定的属性
false - 表示不存在指定的属性
* */
console.log('name' in hero);//true
操作原型的方式
1.利用对象.属性或方法的方式新增属性或方法
Hero.prototype.name = '肖德硕';
Hero.prototype.sayMe = function(){
console.log('this is function');
}
2.将原型重新赋值为一个新对象
Hero.prototype = {
name : '肖德硕',
sayMe : function(){
console.log('this is function');
}
}
代码:
// 定义构造函数
function Hero(){}
// 通过构造函数的原型新增属性或方法
// 1.利用对象.属性或方法的方式新增属性或方法
Hero.prototype.name = '肖德硕';
Hero.prototype.sayMe = function(){
console.log('this is function');
}
// 2.将原型重新赋值为一个新对象
Hero.prototype = {
name : '肖德硕',
sayMe : function(){
console.log('this is function');
}
}
// 通过构造函数创建对象
var hero = new Hero();
console.log(hero.name);
hero.sayMe();
效果:
显式原型与隐式原型
所有对象其实也具有原型
注意 - 对象的原型(__proto__
)并非是函数的原型(prototype)
区分
将函数的原型 -> 显式原型
将对象的原型 -> 隐式原型
对象的原型
不能用于真实开发工作,仅用于逻辑测试
// 定义构造函数
function Hero(){
this.name = '肖德硕';
}
// 通过构造函数的原型新增属性或方法
Hero.prototype.age = 18;
// 通过构造函数创建对象
var hero = new Hero();
console.log(hero.name);// 对象调用自有属性
console.log(hero.age);// 对象调用原型属性
/*
所有对象其实也具有原型
注意 - 对象的原型(__proto__)并非是函数的原型(prototype)
区分
将函数的原型 -> 显式原型
将对象的原型 -> 隐式原型
对象的原型
不能用于真实开发工作,仅用于逻辑测试
*/
console.log(hero.prototype);// undefined 表示对象中不存在该属性
console.log(hero.__proto__);
效果:
isPrototypeOf()方法
每个对象中都会具有一个isPrototypeOf()方法,该方法用来判断一个对象是否是另一个对象的原型
代码:
// 通过初始化器方式定义对象
var obj = {
name : '肖德硕'
}
// 定义构造函数
function Hero(){}
// 将对象obj赋值给构造函数Hero的原型
Hero.prototype = obj;
// 通过构造函数创建对象
var hero = new Hero();
// 判断指定对象是否是另一个对象的原型
var result = obj.isPrototypeOf(hero);
console.log(result);
效果:
扩展內建对象
JavaScript中的内置对象有些也具有prototype属性,利用内置对象的prototype属性可以为内置对象扩展属性或方法
通过原型扩展内置对象的属性和方法非常灵活,根据个性化要求制定JavaScript语言的具体内容。
一般建议慎用这种方式,如果JavaScript的版本更新时可能会提供个性化的属性或方法,导致冲突。
代码:
Object.prototype.sayMe = function(){
console.log('this is sayMe function');
}
// 通过Object构造函数创建对象
var obj = new Object();
obj.sayMe();
Array.prototype.inArray = function(color){
// this - 表示当前的数组
for(var i = 0, len = this.length; i < len; i++){
if(this[i] === color){
return true;
}
}
return false;
}
var arr = ["red", "green", "blue"];
console.log(arr.inArray("red")); //true
console.log(arr.inArray("yellow")); //false
效果: