说原型之前先说说对象,好像在工作中,对象用的挺多的,原型基本上没有用。既然没有用那我还要不要学习呢?思考了很久,还是学一学,万一以后的工作用的着呢?领导常说,上一份工作是为下一份工作做准备的,所以在工作中要多学东西,不要因为暂时不用,而放弃学习,目光要放得长远。既然这样我就简单学一学原型。说原型之前先温习一下对象的相关知识
对象:属性值的集合,属性可以包括基本值,对象,或者函数
原型:在对象上面添加特定的属性,是定义属性的快捷方式,可以假想成 java
里面的类 (ps:Vue
实例就是在 vue
对象上面进行原型扩展
了解原型之前,先简单的介绍一下对象的创建
- 1.对象字面量
var person = {
name: "Nice,
age: 23
}
复制代码
优点:代码量少,给人封装数据的感觉,也可以向函数传递大量可选参数;在实际开发中常用
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
,Value
,get
,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
,引一发而动全身
解决原型对象的问题
- 构造函数和原型模式相结合:引用类型值的属性放在构造函数当中,其他共享的不会修改属性放在原型对象上
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)