JavaScript原型Prototype详情

来自:网络
时间:2022-01-09
阅读:
目录

1、概述

1.1原型是什么

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

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

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

示例代码验证如下:

function fun() {
  console.log('函数原型')
}
console.log(fun.prototype) // {}

fun.prototype返回的也是一个空对象,但是这不说明Object.prototype中没有属性或者方法,这些属性和方法为不可枚举的,示例代码如下所示:

function fun() {
  console.log('函数原型')
}

console.log(fun.prototype) // {}
// 通过 Object.getOwnPropertyNames() 获取全部属性
console.log(Object.getOwnPropertyNames(fun.prototype)) // [ 'constructor' ]

其中,constructor属性指向该构造函数的引用,代码如下:

// constructor属性

console.log(fun.prototype.constructor) // [Function: fun]

console.log(fun.prototype.constructor === fun) // true

1.2获取原型

了解了原型的概念以及作用之后,我们需要获取到原型才能对其进行操作,在JavaScript中获取原型的方式有两种,如下所示:

通过构造函数的prototype属性。

通过Object.getPrototypeOf(obj)方法。

这两个的区别就是构造函数的prototype属性一般只配合构造函数使用,而Object.getPrototypeOf(obj)方法一般是获取构造函数实例化后的对象的原型方法。

实例代码如下:

// 构造函数
function Person(name) {
  this.name = name
}

// 指向构造函数的原型
var p1 = Person.prototype

var person = new Person('一碗周')

// 指向构造函数的原型
var p2 = Object.getPrototypeOf(person)

console.log(p1 === p2) // true

获取原型后可以跟操作对象似的进行操作,因为原型本身就是一个对象。

2、原型属性

JavaScript中,函数本身也是一个包含了方法和属性的对象。接下将学习函数对象的另一个属性——prototype,这个属性的初始值是一个空对象。

2.1利用原型添加属性与方法。

为对象添加属性和方法的另一种用法就是通过原型为其添加。当为一个构造函数添加原型属性和原型方法时,通过该构造函数new出的所有对象共享该属性和方法。

PS:所谓的原型属性或者原型方法就是通过原型添加的属性或者方法。

添加属性和方法的方式具有如下几种方式

直接为其增加属性或者方法

通过Object.defineProperty()方法,添加属性或者方法。这种方式比第一种方式更具有安全性。

直接添加对象到原型。

示例代码如下所示:

//构造函数
function Fun() {}
//直接为构造函数添加属性和方法
Fun.prototype.str = '这是一个字符串'
Fun.prototype.fn = function () {
  console.log('这是一个方法')
}
//通过 defineProperty 添加属性或者方法
Object.defineProperty(Fun.prototype, 'MyFun', {
  value: function () {
    console.log('this is MyFun')
  },
})
//测试
console.log(Fun.prototype.str)
Fun.prototype.fn()
Fun.prototype.MyFun()
var fun = new Fun()
fun.MyFun()
//直接为其定义个对象覆盖到之前的原型上
Fun.prototype = {
  name: '一碗周',
  fun: function () {
    console.log('this is function')
  },
}
Fun.prototype.fun()
var fun = new Fun()
fun.fun()

2.2访问原型属性原型方法

对于原型来说,最重要的一点就是它的实时性 。由于在JavaScript中,几乎所有的对象都是通过传引用的方式来传递的,因此我们所创建的的每个新对象实体中并没有一份属于自己的原型副本。这就意味着我们随时修改prototype属性,并且由同一构造器创建的所有对象的prototype属性也都会同时改变,包括我们之间通过构造器创建的属性和方法。

还是上面那段代码我们向原型中添加一个新方法,并调用,示例代码如下所示:

Fun.prototype.fn = function () {
  console.log('这是一个方法')
}
fun.fn() //这是一个方法

我们之前创建的对象可以访问新加的原型属性和原型方法。

3、自有属性与原型属性

我们先来创建一个构造函数,并为其添加两个原型属性。

//构造函数
function Fun() {}
//添加原型属性和方法
Fun.prototype.name = '一碗粥'
Fun.prototype.print = function () {
  console.log('this is function')
}

在通过该构造函数创建一个对象,为其设置属性和方法

//通过构造函数创建对象
var fun = new Fun()
//为对象添加属性和方法
fun.name = '一碗周'
fun.SayMe = function () {
  console.log('this is SayMe')
}

现在我们的fun对象,拥有自有属性/方法两个,原型属性/方法两个。我们依次来访问这些属性和方法。

//访问属性和方法
console.log(fun.name) // 一碗周
fun.SayMe() // this is SayMe
fun.print() // this is function


当我们访问name属性时,JavaScript引擎会遍历fun这个对象的所有属性,并返回name属性的值。SayMe()方法也是样的道理。但是到print()方法时,JavaScript引擎还是会遍历这个对象所有属性,这时就找不到一个叫print()方法了,接下来JavaScript引擎就会访问创建当前对象的构造器函数的原型,也就是我们的Fun.prototype,如果其中有该属性,则立即返回,否则返回undefined或者抛出异常

结论:当有自有属性时,优先访问自有属性,访问完自有属性再去访问原型属性 。

3.1检测自有属性或者原型属性

现在已经知道自有属性和原型属性的概念以及用途了,但是我们怎么知道一个属性时自由属性还是原有属性,JavaScript中提供以下两种方式来检测一个属性的情况

使用Object.prototype.hasOwnProperty(prop)方法来检测prop属性是否是自由属性,该方法返回一个布尔值,如果是自有属性则返回true,否则返回false。

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

测试代码如下:

// 通过Object.prototype.hasOwnProperty(prop)方法检测是否为自有属性
console.log(fun.hasOwnProperty('name')) // true
console.log(fun.hasOwnProperty('print')) // false
// 如果一个不存在的属性检测结果也是为false
console.log(fun.hasOwnProperty('SayMe')) // true

// 通过 in 运算符
console.log('name' in fun) // true
console.log('print' in fun) // true
console.log('SayMe' in fun) // true

通过测试我们发现,这两个方法并不能检测一个属性是不是一个自有属性或者原型属性,但是将这两个方法结合起来就可以检测是自有属性还是原型属性了,

示例代码如下:

function DetectionAttributes(obj, attr) {
  if (attr in obj) {
    if (obj.hasOwnProperty(attr)) {
      // 如果是自有属性属性返回1
      return 1
    } else {
      // 如果是原型属性返回0
      return 0
    }
  } else {
    // 没有这个属性返回 -1
    return -1
  }
}


测试如下:

console.log(DetectionAttributes(fun, 'name')) // 1
console.log(DetectionAttributes(fun, 'print')) // 0
console.log(DetectionAttributes(fun, 'SayMe')) // 1

4、isPrototypeOf()方法

isPrototypeOf()方法用来检测一个对象是否存在于另一个对象的原型链中,如果存在就返回true,否则就返回false。

实例代码如下所示:

// 定义一个对象,用于赋值给原型对象
var obj = function () {
  this.name = '一碗周'
}
var Hero = function () {} // 定义构造函数
// 将定义的对象赋值给构造函数的原型
Hero.prototype = obj

// 通过Hero创建对象
var hero1 = new Hero()
var hero2 = new Hero()
// 判断创建的两个对象是否在 obj 的原型链中
console.log(obj.isPrototypeOf(hero1)) // true
console.log(obj.isPrototypeOf(hero2)) // true

5、扩展内置对象

JavaScript中的内置对象有些也具prototype属性,利用内置对象的prototype属性可以为内置对象扩展属性和方法。

通过原型扩展内置对象的属性和方法非常灵活,根据个性化要求制定JavaScript语言的具体内容。

扩展内置对象的方式有两种,具体如下所示:

通过直接新增属性和方法。

通过Object对象的defineProperty()方法为其新增属性或者方法。这种方式会比第一种方式要好,是创建的属性和方法更具有安全性

示例代码如下所示:

// 为 Object 扩展属性和方法
// 使用第一种方式
Object.prototype.MyPrint = function () {
  console.log('this is MyPrint()')
}
// 使用第二种方式
Object.defineProperty(Object.prototype, 'MyInput', {
  value: function () {
    console.log('this is MyInput()')
  },
})
// 调用
Object.prototype.MyPrint() // this is MyPrint()
Object.prototype.MyInput() // this is MyInput()

6、结语

这篇文章介绍了JavaScript中的原型的概念、原型属性、如何检测自有属性和原型属性以及如何扩展内置对象

返回顶部
顶部