深入了解原型

栏目: JavaScript · 发布时间: 6年前

内容简介:说原型之前先说说对象,好像在工作中,对象用的挺多的,原型基本上没有用。既然没有用那我还要不要学习呢?思考了很久,还是学一学,万一以后的工作用的着呢?领导常说,上一份工作是为下一份工作做准备的,所以在工作中要多学东西,不要因为暂时不用,而放弃学习,目光要放得长远。既然这样我就简单学一学原型。说原型之前先温习一下对象的相关知识对象:属性值的集合,属性可以包括基本值,对象,或者函数原型:在对象上面添加特定的属性,是定义属性的快捷方式,可以假想成

说原型之前先说说对象,好像在工作中,对象用的挺多的,原型基本上没有用。既然没有用那我还要不要学习呢?思考了很久,还是学一学,万一以后的工作用的着呢?领导常说,上一份工作是为下一份工作做准备的,所以在工作中要多学东西,不要因为暂时不用,而放弃学习,目光要放得长远。既然这样我就简单学一学原型。说原型之前先温习一下对象的相关知识

对象:属性值的集合,属性可以包括基本值,对象,或者函数

原型:在对象上面添加特定的属性,是定义属性的快捷方式,可以假想成 java 里面的类 (ps: Vue 实例就是在 vue 对象上面进行原型扩展

深入了解原型

了解原型之前,先简单的介绍一下对象的创建

  • 1.对象字面量
var person = {
    name: "Nice,
    age: 23
}
复制代码

优点:代码量少,给人封装数据的感觉,也可以向函数传递大量可选参数;在实际开发中常用

    1. new 操作符后跟 Object 构造函数(几乎不用)
var person=new Object();
person.name="Nice;
person.age=23;
复制代码
  • 3.工厂模式(几乎不用)
function createPerson(name,age){
    var o=new Object();
    o.name=name;
    o.age=age;
    return o;
}
var person=createPerson("Nice",23);
复制代码

用一个函数把构造函数包裹起来,再在该函数体内返回改该构造函数

  • 4.构造函数模式(很少用)
function Person(name,age){
    this.name=name;
    this.age=age;
    this.sayName=function(){
        this.name;
    }
}
var person=new Person("Nice",23);
复制代码

构造函数始终都应该以一个大写字母开头,构造函数它本身就是一个函数,如果没有 new 关键字,他就和普通的函数调用一模一样; 调用构造函数经历四个步骤:

1.创建一个新对象

2.将构造函数的作用域赋给新对象( this 就职向这个新对象)

3.执行构造函数中的代码

4.返回新的对象

构造函数创建对象很方便,但也有他的问题:每个方法都要在每个实例上都要重新创建创建一遍,不同的实例的同名函数是不相等;因此创建了大量的重复代码(当你 new 一个构造器对象,上面的构造函数就执行一遍,每次都会新建一个 function ,会新开辟一个内存空间,每次都是指向一个新的堆的对象 ,这样占用内存消耗非常的大);即重复造轮子,我们希望的是代码尽可能的复用,这时候我们就需要原型(实例,构造函数,原型对象之间的关系请看下面)

原型所定义的属性和功能会自动应用到对象的实例上(每个对象都有一份原型引用)

function Animal(name,age) {
  //加一个 this 条件判断,用 instanceof 来检查自己是否被 new 调用
  if (this instanceof Animal) {
    this.name = name
    this.age = age
  }eles {
    //以 new 递归调用自己来为对象创建正确的实例,目的保持没有 new 和有new 的表现行为一致
    return new Animal(name,age)
  }
}
复制代码

所有函数在初始化的时候都有一个 prototype 属性,该属性的初始值是一个空对象,只有函数在作为构造函数的时候, prototype 属性指向原型对象,这个对象包含所有实例共享的属性和方法

所有原型对象会自动获取一个 constructor 属性,指向构造函数

深入了解原型

对象与函数原型之间的引用关系是在对象创建时建立的

由此可以看出,实例和构造函数之间没有什么直接的关系

深入了解原型
function Person(){}
Person.prototype.dance=function(){}
function Ninja(){}
Ninja.prototype={dance:Person.prototype.dance}
复制代码

注意: Person.prototype 设置为一个对象字面量形式创建的新对象时,就切断了原来对象的联系(即 constructor 属性不在指向 Person

深入了解原型

如果 constructor 的值很重要可以向在新对象中设置:

深入了解原型

造成这个原因是, 实例和原型之间的松散链接关系 ,实例中的指针只指向原型,而不指向构造函数(可以看上面,原型,实例,构造函数直接的关系)

但是重设 constructor 属性,会导致它的 [[Enumerable]] 的特性被设为 true ,最好使用 Object.defineProperty()

Object.defineProperty(Person.prototype,'constructor',{
  enumerable:false,
  value:'',
  writable:true
})
复制代码

Object.defineProperty() : 这个方法接受三个参数,属性所在的对象,属性的名字,一个描述符对象;其中描述符对象的属性的一个或者多个值( Configurable , Enumerable , Writable , Valueget , set ),一旦 configurable 定义为不可配置的( false ),就不能把它修改成可配置的,返回被传递的对象

Object.defineProperties() :一次性可修改多个属性,第一个参数是属性对象,第二个参数是所要修改的数据属性组成的集合(即要修改的数据对象),返回被传递的对象

Object.getOwnPropertyDescriptor() :读取属性描述符;第一个参数是属性所在的对象,第二个是要读取其描述符的属性名称,返回一个对象

构造函数内部的绑定操作符优先级永远都高于在原型上绑定的操作符优先级,在应用对象的一个属性时,优先检查该对象上本身是否拥有该属性,如果有则直接返回,否则继续在原型上面找,找不到就返回 undefined 。一般情况不会轻易去修改原型对象上的属性,一旦修改就会出现各种问题;

判断属性是实例还是原型的几种常用方法

hasOwnProperty() :如果返回 true ,该属性存在实例当中

person.hasOwnProperty('name');//返回 true,name 属性在 person 实例当中
复制代码

in 操作符,只要返回 true ,该属性就在对象中,也许是原型中,也许是实例当中

'name' in person //返回 true ,该属性存在
复制代码

for-in 循环时,返回所有能够通过对象访问的,可枚举的属性

Object.keys() :返回对象上所有可枚举的 实例属性 组成的字符串数组

function Person(name,age){
    this.name=name;
    this.age=age;
    this.sayName=function(){
        this.name;
    }
}
Person.prototype.job=function(){

}
var person=new Person();
console.log(Object.keys(person));//返回["name", "age", "sayName"]
复制代码

instanceof 操作符真正的含义:检查右边的构造函数原型是否存在于操作符左边的对象原型上的任何一个位置 object instanceof constructor

深入了解原型
深入了解原型

原型对象的问题

函数的原型是一个对象,所以有很多功能(属性或者方法)可以通过赋值的方法到达继承的目的,同时也可以定义新的方法;

因为原型对象上所有的属性和方法是共享的,而对于属于引用类型值的属性来说,会直接修改原型对象上的属性,造成 bug ,引一发而动全身

解决原型对象的问题

  1. 构造函数和原型模式相结合:引用类型值的属性放在构造函数当中,其他共享的不会修改属性放在原型对象上
深入了解原型

2.动态原型模式:根据函数是否在构造函数中,而选择性的添加到原型对象中去

深入了解原型

3.寄生构造函数

长得和工厂模式一模一样,不同的是,通过 new 操作符来调用

function Person(name,age){
    var o=new Object();
    o.name=name;
    o.age=age;
    return o;
}
var friend=new Person();
复制代码

class 实现继承 class 底层的实现仍然是基于原型继承(ES6语法) 创建类

class Ninja {//创建类名
  constructor(name) {//定义构造函数。当使用关键字 new 调用类时。会调用这个构造函数
    this.name = name 
  }
  swingSword(){//定义一个Ninja 实例均可访问的方法
    return true
  }
}
复制代码

继承类

class Ninja extends Person {//extends 实现继承
 constructor(name.weapon) {
   super(name)
   this.weapon = weapon
 }
 wieldWeapon(){
   return true
 }
}
复制代码

期待我的续更吧,或许写的有点糟糕,我是初学者,如有错误之处,请多多请教(sunseekers_)。掘金谈技术,公众号谈生活(sunseekers)


以上所述就是小编给大家介绍的《深入了解原型》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

超级IP

超级IP

吴声 / 中信出版集团 / 2016-7 / 49.00元

一切商业皆内容,一切内容皆IP! 从迪士尼、airbnb、YouTube、Instagram到微信、Papi酱、芈月传、鹿晗,IP浪潮席卷全球,这不仅仅是互联网领域的革命,更是未来商业的游戏新规则。 IP从泛娱乐形态快速渗透新商业生态全维度,正深化为不同行业共同的战略方法,甚至是一种全新的商业生存方式,即IP化生存。 超级IP的内核,是辨识度极高的可认同的商业符号,它意味着一种对......一起来看看 《超级IP》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码