JavaScript 面向对象之原型

简介: JavaScript 面向对象之原型

原型

概念

在JavaScript中,函数是一个包含属性和方法的Function类型的对象。而原型(Prototype)就是Function类型对象的一个属性。


在函数定义时就包含了prototype属性,它的初始值是一个空对象。在JavaScript中并没有定义函数的原型类型,所以原型可以是任何类型。


原型是用于保存对象的共享属性和方法的,原型的属性和方法并不会影响函数本身的属性和方法。

// Function类型的属性->所有函数都具有的属性
console.log(Function.prototype);//[Function]
// 定义函数
function fn() {
    console.log('this is function');
}
//原型的默认值是空对象
console.log(fn.prototype);//fn {}
// 函数包含构造函数 ——> 所有引用类型其实都是构造函数
console.log(Number.prototype); //[Number: 0]
console.log(Object.prototype);//{}
var result = Object.getOwnPropertyDescriptor(Object.prototype,'constructor');
console.log(result)
//{
//   value: [Function: Object],
//   writable: true,
//   enumerable: false,
//   configurable: true
// }

获取原型

通过如下两种方式可以获取对象的原型,从而设置共享的属性和方法:

通过构造函数的prototype属性

通过Object对象的getPrototype(obj)方法。

function fn() {
    console.log('this is 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 function');
}
// 新增属性或方法
fn.prototype.name = '猪猪侠';
console.log(fn.prototype);//fn { name: '猪猪侠' }
// 使用defineProperty()方法
Object.defineProperty(fn.prototype,'age',{
    value:18,
    enumerable:true//控制当前属性可被枚举(遍历)
});
console.log(fn.prototype);//fn { name: '猪猪侠', age: 18 }

构造函数原型

// 定义构造函数
function Hero() {
    this.name = '猪猪侠';
    this.sayMe = function () {
        console.log('我是猪猪侠')
    }
}
// 操作构造函数的Hero的原型
Hero.prototype.age = 19;
// 利用构造函数方式创建对象
var hero = new Hero();
console.log(hero);//Hero { name: '猪猪侠', sayMe: [Function]
// 为构造函数的原型新增的属性:构造函数创建的对象中依旧可以访问
console.log(hero.age);//19
// 访问对象中不存在的属性是结果为undefined,故对象hero中不存在age属性。
var result = Object.getOwnPropertyDescriptor(hero,'age');
console.log(result)//undefined

解析图如下:

自有属性与原型属性

自有属性:通过对象的引用添加的属性。其它对象可能无此属性;即使有,也是彼此独立的属性。

原型属性:从原型对象中继承来的属性,一旦属性对象中属性值改变,所有继承自该原型的对象属性均改变。

如下代码展示了自由属性和原型属性的用法:

// 定义构造函数
function Hero(name) {
    this.name = name
    this.sayMe = function () {
        console.log('我是:'+ name)
    }
}
// 通过构造函数Hero的prototype新增属性或方法
//通过原型所定义的属性称为:原型属性
Hero.prototype.age = 19;//此时age就是原型属性
/*
通过构造函数Hero创建对象时
    *不仅具有构造函数的自有属性
    *还具有构造函数的原型属性
*/
// 构造函数方式创建对象hero
var hero = new Hero('猪猪侠');
console.log(hero.name);//猪猪侠
console.log(hero.age);//19
var hero2 = new Hero('小菲菲');
console.log(hero2.name);//小菲菲
console.log(hero2.age);//19
/*
// 为对象hero新增age属性
hero.age = 80;
console.log(hero.age);//80
// hero对象的age属性被修改了
console.log(hero);//Hero { name: '猪猪侠', sayMe: [Function], age: 80 }
console.log(hero2.age);//hero2 的age属性值还是19 没改变 ,所以对hero中age属性值的修改对hero2无效
*/
// 通过对Hero的原型操作添加age属性
Hero.prototype.age  = 30;
// 通过对构造函数Hero的原型添加的age属性改变了hero和hero2的age属性值
console.log(hero.age);//30
console.log(hero2.age);//30

重写原型属性

通过构造函数或对象的自有属性可以重写原型的属性,如下代码示例:

// 定义构造函数
function Hero() {
    this.name = '猪猪侠';
}
// 构造函数的原型尝试修改name值
Hero.prototype.name = '超人强';
// 构造函数创建对象
var hero = new Hero();
// 访问hero对象的name属性-- 得出结论:当自有属性与原型属性同名时,默认访问的是自有属性。
// 自有属性的优先级高于原型属性
console.log(hero.name);//猪猪侠
// 删除hero对象的属性
delete hero.name;
// 重新访问hero对象的name属性 :得到的是原型属性,另外可以认为是自有属性的默认值。
console.log(hero.name);//超人强

检测自有或原型属性

使用hasOwnProperty()方法检测对象是否具有指定的自有属性。

使用in关键字检测对象及其原型链中是否具有指定属性。

如下代码示例:

// 定义构造函数
function Hero() {
    // this.name = '猪猪侠';
}
// 原型属性添加name属性,hasOwnProperty(prop)方法判断,结果为false.
Hero.prototype.name = '超人强';
// 构造函数方式创建对象
var hero  = new Hero();
/* 1、使用hasOwnProperty(prop)方法
作用:判断当前属性是否为自有属性
参数:
    prop : 表示指定属性名称。
返回值:
    true :表示存在指定的自有属性
    false :表示不存在指定的自有属性*/
console.log(hero.hasOwnProperty('name'))//true
/*
    2、使用in关键字检测对象的属性
    作用:判断对象中是否存在指定属性(自有属性或原型属性)
    返回值:布尔值
       true :表示存在指定的属性。
       false 表示不存在指定的属性。
*/
console.log('name' in hero);//true

扩展属性或方法

通过原型可以为指定构造函数或对象扩展其属性或方法,如下代码示例:

// 1、定义构造函数
function Hero() {}
// 通过构造函数的原型新增属性或方法
/*// 1、利用对象.属性或方法的方式新增属性或方法
Hero.prototype.name = '猪猪侠';
Hero.prototype.sayMe = function () {
    console.log('我是猪猪侠')
}*/
// 2、将原型重新赋值给一个对象
Hero.prototype = {
    name:'猪猪侠',
    sayMe : function () {
        console.log('我是猪猪侠')
    }
}
//构造函数方式创建对象
var hero = new Hero();
console.log(hero.name)//猪猪侠
hero.sayMe();//我是猪猪侠
// 由结果得出结论两种方式都可通过构造函数的原型新增属性或方法

解析图:

目录
相关文章
|
JavaScript 前端开发 Java
深入JS面向对象(原型-继承)(一)
深入JS面向对象(原型-继承)
31 0
|
1月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承利用原型链查找属性,节省内存但不支持私有成员。类继承通过ES6的class和extends实现,支持私有成员但占用更多内存。两者各有优势,适用于不同场景。
19 0
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(三)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
设计模式 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(一)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
前端开发 JavaScript
JavaScript中的原型和原型链
JavaScript中的原型和原型链
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(二)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
JavaScript 前端开发
JavaScript原型,原型链
JavaScript原型,原型链
|
1月前
|
JavaScript
JS数组增删方法的原理,使用原型定义
JS数组增删方法的原理,使用原型定义
|
1月前
|
JavaScript
JS原型对象prototype
JS原型对象prototype
|
1月前
|
JavaScript 前端开发
深入理解 JavaScript 对象原型,解密原型链之谜(下)
深入理解 JavaScript 对象原型,解密原型链之谜(下)