19原型

原型

原型是什么

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

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

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

原型的默认值是空对象,所有引用类型都是构造函数,所有函数都具有prototype属性。

// Function 类型的属性 -> 所有函数都具有的属性
console.log(Function.prototype);

function fn() {
  console.log("this is function");
}
// 原型的默认值是空对象
console.log(fn.prototype);
// 函数包含构造函数 -> 所有引用类型都是构造函数
console.log(Number.prototype);
console.log(Object.prototype);

var result = Object.getOwnPropertyDescriptor(Object.prototype, "constructor");
console.log(result);

获取原型

获取原型的方式有两种

使用访问对象的属性语法结构

function fn() {
  console.log("this is function ");
}
// 使用访问对象的属性语法结构
console.log(fn.prototype); // fn {}
console.log(fn["prototype"]); // fn {}

Object类提供的getPrototype()方法

function fn() {
  console.log("this is function ");
}
console.log(Object.getPrototypeOf(fn)); //[Function]或者ƒ () { [native code] }

第二种方式中,在nodejs环境中会打印[Function]而在浏览器会打印ƒ () { [native code] }

新增属性和方法

使用对象新增方式。

function fn() {
  console.log("this is function ");
}
// 新增属性或方法
fn.prototype.name = "张无忌";
console.log(fn.prototype.name); // 张无忌
console.log(fn.prototype); // fn { name: '张无忌' }

使用defineProperty()方法

function fn() {
  console.log("this is function ");
}
// defineProperty()
Object.defineProperty(fn.prototype, "age", {
  value: 10,
  // 如果不修改此属性描述符,则不可枚举
  enumerable: true,
});
console.log(fn.prototype); // fn {age: 10 }

构造函数的原型

// 定义构造函数

function Hero() {
  this.name = "张无忌";
  this.sayMe = function () {
    console.log("this is function ");
  };
}
// 操作构造函数Hero的原型
Hero.prototype.age = 18;
//  利用构造函数来创造对象
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

以上代码经测试可以发现,为构造函数原型新添加的属性,在其对象中虽可访问但通过getOwnPropertyDescriptor方法是得不到的。

原型属性

自有属性与原型属性

  • 自有属性:通过对象的引用添加的属性。其它对象可能无此属性;即使有,也是彼此独立的属性。
  • 原型属性:从原型对象中继承来的属性,一旦原型对象中属性值改变,所有继承自该原型的对象属性均改变。

通过构造函数Hero创建对象时不仅具有构造函数的自有属性,也具有构造函数的原型属性。

通过对象是无法修改原型的值,修改原型的值,必须修改构造函数的原型。

// 定义一个构造函数
function Hero(name) {
  // 构造函数本身的属性 成为自有属性
  this.name = name;
  this.sayMe = function () {
    console.log("this is function");
  };
}
// 通过构造函数Hero的prototype新增属性或方法
// 通过原型所定义的属性称为原型属性
Hero.prototype.age = 19;
/**
 * 通过构造函数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 = 80; // 相当于为hero新增属性
console.log(hero);
console.log(hero.age); //80

console.log(hero2.age); // 19

Hero.prototype.age = 80;

自有属性与原型属性的优先级

自有属性的优先级于原型属性,也就是说当原型属性与自有属性同时存在时,那么优先打印出的是自有属性。但自有属性不会覆盖原型属性,当删除自有属性时,再次访问该属性,那么会输出原型属性。

// 定义构造函数
function Hero() {
  this.name = "张无忌";
}
// 构造函数的原型
Hero.prototype.name = "周芷若";
// 构造函数创建对象
var hero = new Hero();
// 自有属性与原型同名时,默认访问的是自有属性
// 自有属性的优先级别高于原型属性
console.log(hero.name); //张无忌
// 删除对象的属性
delete hero.name;
console.log(hero.name); //周芷若

检测自有属性与原型属性

检测自有属性有两种方式,分别为object.hasOwnProperty(prop)in关键字。但这两种方式是有区别的。

  • object.hasOwnProperty(prop)方式
    • 参数 示指定属性的名称,字符串类型
    • 返回值
      • true 表示存在指定属性是自有属性
      • false 表示不存在指定的自有属性
    • 示例
function Hero() {
  this.name = "张无忌"; // 自有属性
}
Hero.prototype.age = 18;
var hero = new Hero();
/**
 * object.hasOwnProperty()
 * 作用 - 判断当前属性是否为自有属性
 *  参数
 *    prop - 表示指定属性的名称
 * 返回值
 *   true - 表示存在指定属性是自有属性
 *   false - 表示不存在指定的自有属性
 */
console.log(hero.hasOwnProperty("name")); //true
console.log(hero.hasOwnProperty("age")); // false
console.log(hero.hasOwnProperty("sex")); // false
  • in关键字
    • 返回值
      • true 表示存在指定属性
      • false 表示不存在指定属性
    • 示例
function Hero() {
  this.name = "张无忌"; // 自有属性
}
Hero.prototype.age = 18;
var hero = new Hero();
/**
 * 使用in关键字检测对象的属性
 * 作用 - 判断对象中是否存在指定属性(自有属性或原型属性)
 *  * 返回值
 *   true - 表示存在指定属性
 *   false - 表示不存在指定属性
 */
console.log("name" in hero); //true
console.log("age" in hero); //true
console.log("sex" in hero); // false

扩展属性和方法

  1. 利用对象.属性或方法的方式新增属性或方法
function Hero() {}
// 1. 利用对象.属性或方法的方式新增属性或方法
Hero.prototype.name = "张无忌";
Hero.prototype.sayMe = function () {
    console.log("this is function");
};
var hero = new Hero();
console.log(hero.name); // 张无忌
hero.sayMe(); // this is function
  1. 直接将原型重新赋值为一个新对象
function Hero() {}
// 2. 直接将原型重新赋值为一个新对象
Hero.prototype = {
    name: "张无忌",
    sayMe: function () {
        console.log("this is function");
    },
};
var hero = new Hero();
console.log(hero.name); // 张无忌
hero.sayMe(); // this is function
  1. 二者区别 第一种是相当于在原有的基础上扩充,而第二种会直接将内存指向改为一个新对象,如果原来新增过属性或方法,则全会丢失。

隐式原型与显式原型

将函数的原型称为显式原型

将对象的原型成为隐式原型

所有对象都具有原型,但对象的原型(__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__); // Hero { age: 18 }

isPrototypeOf()方法

isPrototypeOf() 判断指定对象是否是另一个对象的原型。

// 通过初始化器定义对象
obj = {
  name: "张无忌",
};
// 定义构造函数
function Hero() {}
// 将对象obj赋值给构造函数Hero的原型
Hero.prototype = obj;
// 通过构造函数创建对象
var hero = new Hero();
// isPrototypeOf() 判断指定对象是否是另一个对象的原型
console.log(obj.isPrototypeOf(hero)); // true
console.log(hero.isPrototypeOf(obj)); // fasle

扩展内建对象

  1. 直接通过Object原型新增
Object.prototype.sayMe = function () {
  console.log("this is function");
};
// 通过Object构造函数创建对象

var obj = new Object();

obj.sayMe();
  1. 通过defineProperty方法新增
Object.defineProperty(Object.prototype, "sayMe", {
  value: function () {
    console.log("this is sayMe");
  },
});
var obj = new Object();
obj.sayMe();

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 四、mongoose的使用

    2.定义路由 分模块开发,将路由的方法写在/constroller/stu.js文件中。

    Dreamy.TZK
  • JavaScript中的继承链

    Dreamy.TZK
  • 十、promise的使用

    Promise对象是一个构造函数,用来生成Promise实例。Promise构造函数接收一个函数作为参数。这个参数的函数又有两个参数,这两个参数分别是resol...

    Dreamy.TZK
  • 单例模式简单复杂,线程不安全到安全

    说到单例模式,可以说单例模式是最常见,也是最常用的设计模式了。Spring的bean默认就是单例的。虽然单例模式是最简单的设计模式,但是在实现上有多种方式,分别...

    java乐园
  • 年度必读:2018最具突破性人工智能论文Top 10

    网友推荐的Statistical Modeling: The Two Cultures大受好评,强调经典统计数据和ML预测/建模之间的重要区别。

    新智元
  • 谷歌高级研究员Nature发文:避开机器学习三大「坑」

    机器学习正在推动各个科学领域的研究进展,其强大的模式发现和预测工具正在助力所有领域的科学家——从寻找合成分子到提升医学诊断效果再到揭示基本粒子,可谓应有尽有。

    机器之心
  • 双节祝福模板上线、直播新增强制签到,快来体验新特性!

    ? 十一假期即将到来,每个节假日到来前,各个公司的乐享社区一定非常活跃。为了满足更多需求,腾讯乐享又更新了一批新功能! 乐乐带你一起看看这次更新的3个直播特性...

    腾讯乐享
  • Imperva创新机器学习:领先威胁一步

    在过去的两年里,企业生产的数据量超过了之前整个人类历史创造的数据总和。要为如此大规模的数据提供安全保障,专家们不得不重新思考,该以何种方式决定敏感文件的授权与撤...

    企鹅号小编
  • 深度学习思考

    大部分机器学习算法(包括深度学习),其实是在一个理想空间里(接下来我们会以三维空间为例子)寻找一个最大/最小值。三维空间是无限大的,在某个实际场景,假设我们有了...

    用户2936994
  • 机器学习基础与实践(一)——数据清洗

    想写这个系列很久了,最近刚好项目结束了闲下来有点时间,于是决定把之前学过的东西做个总结。之前看过一些机器学习方面的书,每本书都各有侧重点,机器学习实战和集体智慧...

    CDA数据分析师

扫码关注云+社区

领取腾讯云代金券