javaScript抱佛脚之构造函数、原型等等

算法星轨师
• 阅读 786

一、创建对象

创建对象的发展史:

最早

var person = new Object()
person.name = 'Green'

对象字面量

var person = {
    name = 'Green',
    age = '25',
    sayName: function(){
        alert('this.name')
    }
}

以上两种都会有大量重复性的代码,于是乎:

工厂模式

function createPerson(name, age, job){
    var o = new Object();   // 这个叫做显式的创造对象
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function(){
        alert('this.name')
    };
    return o;
}
var example = createPerson('Green',25,'无业!');

虽然解决了重复代码问题,但没有解决对象识别(工厂模式无从识别对象的类型,因为全部都是Object,不像Date、Array等,本例中,得到的都是o对象,对象的类型都是Object,因此出现了构造函数模式)

构造函数模式

function Person(name,age,family) {
    this.name = name;
    this.age = age;
    this.family = family;
    this.say = function(){
        alert(this.name);
    }
}
var person1 = new Person("lisi",21,["lida","lier","wangwu"]);
var person2 = new Person("lisi",21,["lida","lier","lisi"]);
/* 这是在创建Person的实例,必须用到new

 - 创建一个新对象
 - 将构造函数的作用域赋过去(this指向新对象)
 - 执行构造函数的代码
 - 返回新对象 */

console.log(person1 instanceof Object); //true
console.log(person1 instanceof Person); //true

算是构造函数的特点?
。没有显式的创建对象(拗口)
。将属性方法赋给了this对象
。没有return

instanceof: 识别对象类型
在全局作用域中调用一个函数时,this永远指向window (踩坑了~)

构造函数模式内的方法每次都会在实例上重建一遍,里面的方法在做同一件事,但是实例化后却产生了不同的对象,方法是函数 ,函数也是对象。但如果相同的方法都写在全局作用域里,会产生很多全局函数,失去了这个引用类型的封装性。所以就产生了:

原型模式

function Person() {
}

Person.prototype.name = "lisi";
Person.prototype.age = 21;
Person.prototype.family = ["lida","lier","wangwu"];
Person.prototype.say = function(){
    alert(this.name);
};
console.log(Person.prototype);   //Object{name: 'lisi', age: 21, family: Array[3]}

var person1 = new Person();        //创建一个实例person1
console.log(person1.name);        //lisi

var person2 = new Person();        //创建实例person2
person2.name = "wangwu";
person2.family = ["lida","lier","lisi"];
console.log(person2);            //Person {name: "wangwu", family: Array[3]}
// console.log(person2.prototype.name);         //报错
console.log(person2.age);              //21

~ 每个函数有一个prototype属性,指向一个对象(该函数的原型对象),用途是包含了一些属性和方法等信息,可以被一些由调用该函数创建的实例所共享。这些信息不必定义在构造函数内,只要添加到原型对象上即可。
~ 而原型对象都会获得一个constructor(构造函数)属性,这是一个指向prototype属性所在函数的指针。(prototype和constructor属性在函数与原型之间互相指)
~ 而创建出的实例内部,又有一个指针,指向原型对象(和构造函数里的prototype指的一样,其实实例与构造函数无关,与他的原型有关),是你吗__proto__?

检测属性

  • 使用 hasOwnProperty() 方法可以检测一个属性是存在于实例还是他的原型中。给定属性存在于实例中会返回true。
  • in操作符:单独使用时,无论属性存在于哪里,只要有就是true

for-in循环使用时,返回所有能够通过对象访问的可枚举属性,实例和原型的都包括。入所需要取得对象上所有可枚举的实例属性,推荐Object.key()方法。【深拷贝用过】

原型模式的优点是共享,缺点也是共享(过度)。比如两个实例由调用同一个构造函数得来,其中一个实例修改了原型对象上属性值 ,另一个实例也会共享这个修改。所以:

组合使用构造函数模式和原型模式

简单来说就是构造函数里面定义实例属性,原型模式定义共享属性。


原型链

简单描述,就是将一个构造函数的实例赋值给另一个构造函数的原型对象。层层套在一起成为一个链条。是实现继承的方法。

原型链的继承仍然存在共享过度的问题,除此之外子类型实例不能给超类型传递参数。于是我们就要用到:

借用构造函数

基本思想: 在子类型构造函数内部调用超类型构造函数(通过call apply方法)
但是这样方法又必须全定义在构造函数里,又不能复用了。于是就又有了:

组合继承

基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。这样既通过在原型上定义方法实现了函数的复用,又能够保证每个函数都有自己的属性。

function SuperType(name) {
    this.name = name
    this.color = ['red', 'blue']
}
SuperType.prototype.getName = function() {
    console.log(this.name)
}

function SubType(name, age) {
    SuperType.call(this, name)  // 继承属性
    this.age = age
}
SubType.prototype.getAge = function() {
    console.log(this.age)
}

SubType.prototype = new SuperType()  // 继承方法
SubType.prototype.constructor = SubType

var instance1 = new SubType('zhangsan', 18)
instance1.colors.push('black')
console.log(instance1.colors)  // 'red', 'blue', 'black'
console.log(instance1.getName)  // 'zhangsan'
console.log(instance1.getAge)   // 18

var instance2 = new SubType('lisi', 20)
console.log(instance2.colors)   //  'red', 'blue'
console.log(instance2.getName)  // 'lisi'
console.log(instance2.getAge)  // 20

属性类型

1、数据属性

有四个描述特性:

  • configurable 能否删除属性 (*置为false后就无法再改变)
  • enumerable 能否通过for-in循环返回属性
  • writable 能否修改属性值
  • value 从这里读取或者写入属性值

前三项默认值都为true,如果需要修改,需要调用大名鼎鼎的Object.defineProperty()方法

var person = {}
Object.defineProperty(person,'name',{  // 三个参数
    writable: false,  // 这里如果不指定都默认为false
    value: 'Green'
})
alert(person.name);  // Green
person.name = 'Blue'
alert(person.name);  // Green

2、访问器属性

包含一对getter(读取访问器属性时调用)和setter(写入访问器属性时调用)函数
有四个描述特性:

  • configurable 一样
  • enumerable 一样
  • get 读取属性时调用的函数
  • set 写入属性时调用的函数

仍需调用Object.defineProperty()方法来定义

var book = {
    _year :2004,
    edition: 1
}
Object.defineProperty(book,'year',{
    get: function(){
        return this._year;
    }
    set: function(newValue){
        if(newValue > 2004){
            this._year = newValue;
            this.edition += newValue - 2004;
        }
    }   
})
book.year = 2005
alert(book.edition);  // 2

这是使用访问器属性的常见方式,即设置一个属性的值会导致其他属性发生变化。
*_year的下划线表示只能通过对象方法访问(不懂,等我再查查)

点赞
收藏
评论区
推荐文章
待兔 待兔
10个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
菜园前端 菜园前端
2年前
前端学习重点-原型与原型链
原文链接:什么是原型?每一个函数都包含一个prototype属性,这是一个指针指向"原型对象"(prototypeobject),也就是我们平时说的原型。每一个函数都包含不同的原型对象。当将函数用作构造函数的时候,新创建的对象(实例)会从原型对象上继承属性
Stella981 Stella981
3年前
JavaScript prototype原型用法
JavaScript对象原型所有JavaScript对象都从原型继承属性和方法。<!DOCTYPEhtml<html<metacharset"utf8"<titlejs</title<body<h2JavaScript对象</h2
Stella981 Stella981
3年前
JavaScript原型深入浅出
不学会怎么处理对象,你在JavaScript道路就就走不了多远。它们几乎是JavaScript编程语言每个方面的基础。事实上,学习如何创建对象可能是你刚开始学习的第一件事。对象是键/值对。创建对象的最常用方法是使用花括号{},并使用点表示法向对象添加属性和方法。letanimal{}animal.name
Stella981 Stella981
3年前
JavaScript基础之创建对象
!(https://oscimg.oschina.net/oscnet/361b5f13d45b403ebff39a63013a7644.png)JavaScript对象的创建!(https://oscimg.oschina.net/oscnet/c5d755a12cca4c628dba372da7d7ab3b.jpg)在Ja
Wesley13 Wesley13
3年前
JS原型、原型链深入理解
原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对
Stella981 Stella981
3年前
JavaScript 基于原型链的继承
JavaScript对象是动态的属性“包”(指其自己的属性)。JavaScript对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。遵循ECMAScript标准,someObject.Prototype
Stella981 Stella981
3年前
JavaScript面向对象编程的15种设计模式
在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”。在JavaScript中并没有类这种概念,面向对象编程不是基于类,而是基于原型去面向对象编程,JS中的函数属于一等对象,而基于JS中闭包与弱类型等特性,在实现一些设计模式的方式上与众不同。ps:本文之讲述面向对象编程的设计模式策略,JavaScript原型的基础请参考阮一峰面向
Stella981 Stella981
3年前
Pre
PAT甲级1119,我先在CSDN上面发布的这篇文章:https://blog.csdn.net/weixin\_44385565/article/details/89737224(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fblog.csdn.net%2Fweixin_443855
Wesley13 Wesley13
3年前
Unity 获取场景中所有目标对象(包括不激活的对象)
usingUnityEngine;usingUnityEditor;usingSystem.Collections.Generic;publicclassExampleScript:MonoBehaviour{//获取场景中所有目标对象(包括不激活的对象)不包括Pre
Wesley13 Wesley13
3年前
JS创建对象模式7种方法详解
创建对象的几种模式虽然Object构造函数或者字面量,都可以用来创建对象,但这些方式有明显的缺点:使用同一个接口创建很多对象,会产生大量的代码,于是,工厂模式诞生了1工厂模式工厂模式是广为人知的设计模式,抽象了创建具体对象的过程。在ES6的Class创建类之前,是无法创建类的,开发人员就发明了一种函数,用函数来封