详解js创建对象的几种方法及继承


Posted in Javascript onApril 12, 2019

创建对象

通过Object构造函数或对象字面量创建单个对象
这些方式有明显的缺点:使用同一个接口创建很多对象,会产生大量的重复代码。为了解决这个问题,出现了工厂模式。

工厂模式

考虑在ES中无法创建类(ES6前),开发人员发明了一种函数,用函数来封装以特定接口创建对象的细节。(实现起来是在一个函数内创建好对象,然后把对象返回)。

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 0;
}

var person1=createPerson("Nicholas",29,"Software Engineer");
var person2=createPerson("Greg",27,"Doctor");

构造函数模式

像Object和Array这样的原生构造函数,在运行时会自动出现在执行环境。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。

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

var person1=new Person(...);
var person2=new Person(...);

与工厂模式相比,具有以下特点:

  1. 没有显式创建对象;
  2. 直接将属性和方法赋给了this对象;
  3. 没有return语句;
  4. 要创建新实例,必须使用new操作符;(否则属性和方法将会被添加到window对象)
  5. 可以使用instanceof操作符检测对象类型

构造函数的问题:

构造函数内部的方法会被重复创建,不同实例内的同名函数是不相等的。可通过将方法移到构造函数外部解决这一问题,但面临新问题:封装性不好。

原型模式

我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。(prototype就是通过调用构造函数而创建的那个对象实例的原型对象)。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。

function Person(){
}

Person.prototype.name="Nicholas";
Person.prototype.age=29;
Person.prototype.job="...";
Person.prototype.sayName=function(){
  ...
};

var person1=new Person();
person1.sayName();//"Nicholas"

更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象,并重设constructor属性。

function Person(){
}

Person.prototype={
  name:"...",
  age:29,
  job:"...",
  sayName:function(){
    ...
  }
};

Object.defineProperty(Person.prototype,"constructor",{
  enumerable:false,
  value:Person,
});

原型对象的问题:

他省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值,虽然这会在一定程度带来一定的不便,但不是最大的问题,最大的问题是由其共享的本性所决定的。
对于包含基本值的属性可以通过在实例上添加一个同名属性隐藏原型中的属性。然后,对于包含引用数据类型的值来说,会导致问题。

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

这是创建自定义类型的最常见的方式。
构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。所以每个实例都会有自己的一份实例属性的副本,但同时共享着对方法的引用,最大限度的节省了内存。同时支持向构造函数传递参数。

function Person(name,age,job){
  this.name=name;
  this.age=age;
  this.job=job;
  this.friends=["S","C"];
}

Person.prototype={
  constructor:Person,
  sayName:function(){
    alert(this.name);
  }
};

var person1=new Person(...);

动态原型模式

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

  if(typeof this.sayName!="function"){
    Person.prototype.sayName=function(){
      alert(this.name);
    };
  }
}

这里只有sayName()不存在的情况下,才会将它添加到原型中,这段代码只会在初次调用构造函数时才执行。这里对原型所做的修改,能够立刻在所有实例中得到反映。

Object.create()

ES5定义了一个名为Object.create()的方法,它创建一个新对象,其中第一个参数是这个对象的原型,第二个参数对对象的属性进行进一步描述。

Object.create()介绍

Object.create(null) 创建的对象是一个空对象,在该对象上没有继承 Object.prototype 原型链上的属性或者方法,例如:toString(), hasOwnProperty()等方法

Object.create()方法接受两个参数:Object.create(obj,propertiesObject) ;

obj:一个对象,应该是新创建的对象的原型。

propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。

var o = Object.create(Object.prototype, {
 // foo会成为所创建对象的数据属性
 foo: { 
  writable:true,
  configurable:true,
  value: "hello" 
 },
 // bar会成为所创建对象的访问器属性
 bar: {
  configurable: false,
  get: function() { return 10 },
  set: function(value) {
   console.log("Setting `o.bar` to", value);
  }
 }
});
console.log(o);//{foo:'hello'}
var test1 = Object.create(null) ;
console.log(test1);// {} No Properties 
因为在bar中设置了configurable 使用set,get方法默认都是不起作用,所以bar值无法赋值或者获取
这里的o对象继承了 Object.prototype Object上的原型方法
我们可以 对象的 __proto__属性,来获取对象原型链上的方法 如:
console.log(o.__proto__);//{__defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, __lookupSetter__: ƒ, …}
console.log(test1.__proto__);//undefined

通过打印发现, 将{}点开,显示的是 No Properties ,也就是在对象本身不存在属性跟方法,原型链上也不存在属性和方法,

new object()

var test1 = {x:1};

var test2 = new Object(test1);

var test3 = Object.create(test1);
console.log(test3);//{} 
//test3等价于test5
var test4 = function(){
  
}
test4.prototype = test1;
var test5 = new test4();
console.log(test5);
console.log(test5.__proto__ === test3.__proto__);//true
console.log(test2);//{x:1}
var test1 = {};
var test2 = new Object();
var test3 = Object.create(Object.prototype);
var test4 = Object.create(null);//console.log(test4.__proto__)=>undefined 没有继承原型属性和方法
console.log(test1.__proto__ === test2.__proto__);//true
console.log(test1.__proto__ === test3.__proto__);//true
console.log(test2.__proto__ === test3.__proto__);//true
console.log(test1.__proto__ === test4.__proto__);//false
console.log(test2.__proto__ === test4.__proto__);//false
console.log(test3.__proto__ === test4.__proto__);//false

总结:使用Object.create()是将对象继承到__proto__属性上

var test = Object.create({x:123,y:345});
console.log(test);//{}
console.log(test.x);//123
console.log(test.__proto__.x);//3
console.log(test.__proto__.x === test.x);//true

var test1 = new Object({x:123,y:345});
console.log(test1);//{x:123,y:345}
console.log(test1.x);//123
console.log(test1.__proto__.x);//undefined
console.log(test1.__proto__.x === test1.x);//false

var test2 = {x:123,y:345};
console.log(test2);//{x:123,y:345};
console.log(test2.x);//123
console.log(test2.__proto__.x);//undefined
console.log(test2.__proto__.x === test2.x);//false

继承

我这里就介绍一种吧,剩下的可以去权威指南里看去

原型链

ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原 型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每 个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型 对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的 原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数 的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实 例与原型的链条。这就是所谓原型链的基本概念。

实现原型链有一种基本模式,其代码大致如下。

function SuperType(){
    this.property = true;
}
SuperType.prototype.getSuperValue = function(){
  return this.property;
};
function SubType(){
  this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
  return this.subproperty;
  };
  var instance = new SubType();
alert(instance.getSuperValue());
//true

以上代码定义了两个类型:SuperType 和 SubType。每个类型分别有一个属性和一个方法。它们 的主要区别是 SubType 继承了 SuperType,而继承是通过创建 SuperType 的实例,并将该实例赋给 SubType.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原 来存在于 SuperType 的实例中的所有属性和方法,现在也存在于 SubType.prototype 中了。在确立了 继承关系之后,我们给 SubType.prototype 添加了一个方法,这样就在继承了 SuperType 的属性和方 法的基础上又添加了一个新方法。这个例子中的实例以及构造函数和原型之间的关系如图 6-4 所示。

详解js创建对象的几种方法及继承

以上所述是小编给大家介绍的js创建对象的几种方法及继承详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
js使用栈来实现10进制转8进制与取除数及余数
Jun 11 Javascript
两种方法实现在HTML页面加载完毕后运行某个js
Jun 16 Javascript
基于JavaScript创建动态Dom
Dec 08 Javascript
js实现文字滚动效果
Mar 03 Javascript
Ajax分页插件Pagination从前台jQuery到后端java总结
Jul 22 Javascript
Angularjs通过指令监听ng-repeat渲染完成后执行脚本的方法
Dec 31 Javascript
vue-router+vuex addRoutes实现路由动态加载及菜单动态加载
Sep 28 Javascript
react 实现页面代码分割、按需加载的方法
Apr 03 Javascript
微信小程序实现多选框全选与取消全选功能示例
May 14 Javascript
js实现窗口全屏示例详解
Sep 17 Javascript
Angular value与ngValue区别详解
Nov 27 Javascript
微信小程序弹窗禁止页面滚动的实现代码
Dec 30 Javascript
详解JQuery基础动画操作
Apr 12 #jQuery
React中阻止事件冒泡的问题详析
Apr 12 #Javascript
TypeScript中的方法重载详解
Apr 12 #Javascript
vue-cli 3.x配置跨域代理的实现方法
Apr 12 #Javascript
解决微信小程序调用moveToLocation失效问题【超简单】
Apr 12 #Javascript
详解Bootstrap 学习(一)入门
Apr 12 #Javascript
vue组件中iview的modal组件爬坑问题之modal的显示与否应该是使用v-show
Apr 12 #Javascript
You might like
这部番真是良心,画质好到像风景区,剧情让人跟着小公会热血沸腾
2020/03/10 日漫
一个用php实现的获取URL信息的类
2007/01/02 PHP
PHP教程 变量定义
2009/10/23 PHP
PHP文件读取功能的应用实例
2015/05/08 PHP
对于Laravel 5.5核心架构的深入理解
2018/02/22 PHP
Laravel 实现Controller向blade前台模板赋值的四种方式小结
2019/10/22 PHP
jQuery ui 利用 datepicker插件实现开始日期(minDate)和结束日期(maxDate)
2014/05/22 Javascript
Javascript中的五种数据类型详解
2014/12/26 Javascript
javascript模拟命名空间
2015/04/17 Javascript
js判断手机号运营商的方法
2015/10/23 Javascript
React Native中NavigatorIOS组件的简单使用详解
2018/01/27 Javascript
jQuery+koa2实现简单的Ajax请求的示例
2018/03/06 jQuery
浅析Vue 生命周期
2018/06/21 Javascript
Vue中$refs的用法详解
2018/06/24 Javascript
浅谈JS中this在各个场景下的指向
2019/08/14 Javascript
举例详解Python中的split()函数的使用方法
2015/04/07 Python
Python使用PyCrypto实现AES加密功能示例
2017/05/22 Python
使用 Python 实现微信群友统计器的思路详解
2018/09/26 Python
python游戏地图最短路径求解
2019/01/16 Python
Python使用正则表达式分割字符串的实现方法
2019/07/16 Python
如何让python的运行速度得到提升
2020/07/08 Python
基于HTML5 WebGL的3D机房的示例
2018/03/16 HTML / CSS
匡威俄罗斯官网:Converse俄罗斯
2020/05/09 全球购物
27个经典Linux面试题及答案,你知道几个?
2013/01/10 面试题
期末自我鉴定
2014/02/02 职场文书
致100米运动员广播稿
2014/02/14 职场文书
挂职自我鉴定
2014/02/26 职场文书
学习十八大报告感言
2014/02/28 职场文书
保卫科工作岗位职责
2014/03/01 职场文书
信息管理专业自荐书
2014/06/05 职场文书
学雷锋标兵事迹材料
2014/08/18 职场文书
寻衅滋事罪辩护词
2015/05/21 职场文书
心灵点滴观后感
2015/06/02 职场文书
python实现腾讯滑块验证码识别
2021/04/27 Python
JDBC连接的六步实例代码(与mysql连接)
2021/05/12 MySQL
python引入其他文件夹下的py文件具体方法
2021/05/23 Python