【编程向导】JavaScript-创建对象一期讲解

这篇具有很好参考价值的文章主要介绍了【编程向导】JavaScript-创建对象一期讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

工厂模式

工厂模式 是用来创建对象的一种最常用的设计模式。工厂模式不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。工厂模式常见于大型项目,例如 jQuery 的 $ 对象,我们创建选择器对象之所以没有 new selector 就是因为 $() 已经是一个工厂方法,其他例子例如 React.createElement()Vue.component() 都是工厂模式的实现。

工厂模式根据抽象程度的不同可以分为三种:

  • 简单工厂:通过第三方的类完成松耦合的任务
  • 复杂工厂:通过把实例化的任务交给子类来完成的,用以到达松耦合的目的
  • 超级工厂:通过 eval() 来完成智能工厂

工厂的目的:在于判断接口最终用哪个类实例化(故与接口密不可分)。

使用工厂最终达到的效果是:多态,和类与类之间的松耦合。

应用场景

ES5 实现工厂模式

function createPerson(name, age, job) {
  let person = new Object();
  person.name = name;
  person.age = age;
  person.job = job;
  person.sayNam = function () {
    console.log(`I'm ${name}`);
  };

  return person;
}

const person1 = createPerson('Ben', 21, 'student');
const person2 = createPerson('Gray', 25, 'Doctor');

函数 createPerson() 能够根据接受的参数来构建一个包含所有必要信息的 Person 对象。可以无数次调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。

ES6 实现工厂模式

class User {
  constructor(name, auth) {
    this.name = name;
    this.auth = auth;
  }
}
class UserFactory {
  static createUser(name, auth) {
    //工厂内部封装了创建对象的逻辑:
    //权限为 admin 时,auth=1;权限为 user 时,auth 为 2
    //使用者在外部创建对象时,不需要知道各个权限对应哪个字段, 不需要知道赋权的逻辑,只需要知道创建了一个管理员和用户
    if (auth === 'admin') return new User(name, 1);
    if (auth === 'user') return new User(name, 2);
  }
}
const admin = UserFactory.createUser('cxk', 'admin');
const user = UserFactory.createUser('xz', 'user');

原型模式

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

function Person(){}

Person.prototype.name = 'Uzi';
Person.prototype.age = 22;
Person.prototype.job = 'E-Sports Player';
Person.prototype.sayName = function(){
  console.log(this.name);
}

const uzi1 = new Person();
uzi1.sayName();
// 'Uzi'

const uzi2 = new Person();
uzi2.sayName();
// 'Uzi'

// 共用公用方法
console.log(person1.sayName == person2.sayName);
// true

与构造函数不同,新对象的这些属性和方法是由所有实例共享的。

理解原型对象

无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性是一个指向 prototype 属性所在函数的指针。

创建了自定义的构造函数之后,其原型对象默认只会取得 constructor 属性;至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新的实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMAScript 5 中管这个指针叫做 [[Prototype]]。虽然在脚本中没有标准的方式访问 [[Prototype]],但 Firefox、Safari 和 Chrome 在每个对象上都支持一个属性 __proto__;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型之间,而不是存在于实例与构造函数之间。

原型最初只包含 constructor 属性,而该属性也是共享的,因此可以通过对象实例访问。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的属性。

function Person(){}

Person.prototype.name = 'Nicholas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function(){
console.log(this.name);
};

const person1 = new Person();
const person2 = new Person();

person1.name = 'Greg';
console.log(person1.name);
// 'Greg' 		// from instance
console.log(person2.name);
// 'Nicholas' 	// from prototype

两个实例访问 name 属性的过程:

  • person1 ==> 实例中读取 name 属性 ==> 在实例中读取 name 属性成功
  • person2 ==> 实例中读取 name 属性 ==> 实例中无 name 属性 ==> 从原型链中读取 name 属性 ==> 读取成功

当为对象实例添加一个属性时,这个属性就会 屏蔽 原型对象中保存的同名属性。换句话说,添加这个属性只会阻止我们访问原型中的那个属性值,但不会修改那个属性。即使这个属性设置为 null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用 delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性。

ECMAScript5 的 Object.getOwnPropertyDescriptor() 方法只能用于实例属性,要取得原型属性的描述符,必须直接在原型对象上调用 Object.getOwnPropertyDescriptor() 方法。

原型与实例属性检测

有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。在单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true无论该属性存在于实例中还是原型中

同时使用 hasOwnProperty() 方法和 in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

由于 in 操作符只要通过对象能够访问到属性就返回 truehasOwnProperty() 只在属性存在于实例中时才返回 true,因此只要 in 操作符返回 truehasOwnProperty() 返回 false,就可以确定属性是原型中的属性。

更简单的原型语法

前面的例子中每添加一个属性和方法就要输入一遍 Person.prototype,为减少不必要的输入,也为了从视觉上更好地封装原型的功能,更常见的做法是用一个包含所有属性和方法的对象字面量来重写整个原型对象。

function Person(){}

Person.prototype = {
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function (){
    console.log(this.name);
  }
}

前面介绍过,没创建一个函数,就会同时创建它的原型对象,这个对象自动获得构造函数。而这里的语法,这里相当于重写了实例的原型对象,相应地原型对象中的构造函数 constructor 亦被覆盖,不再指向 Person 函数。此时,尽管 instanceof 操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

当然,我们可以手动为它设置回适当的值。但是,以这种方式重设 constructor 属性回导致它的 [[Enumerable]] 特性被设置为 true。默认情况下,原生的 constructor 属性是不可枚举的。

function Person(){}

Person.prototype = {
  constructor: Person,
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function (){
    console.log(this.name);
  }
}

重设构造函数,只适用于 ECMAScript5 兼容的浏览器。

Object.defineProperty(Person, 'constructor', {
  enumerable: false,
  value: Person
})

原型的动态性

由于在原型中查找值的过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来,即使是先创建了实例后修改原型也照样如此。

实例与原型之间的关系是松散的,

function Person(){}

const friend = new Person();

Person.prototype = {
  constructor: Person,
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  sayName: function (){
    console.log(this.name);
  }
};

friend.sayName();
// error

重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系,它们引用的仍然是最初的原型。

原型对象的原型

原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。所有原生引用类型(Object、Array、String 等等)都在其构造函数的原型上定义了方法。

通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。

尽管可以这样做,但我们不推荐在产品化的程序中修改原生对象的原型。如果因某个实现中缺少某个方法,就在原生对象的原型中添加这个方法,那么当在另一个支持该方法的实现中运行代码时,就可能会导致命名冲突。而且,这样做也可能会意外地重写原生方法。

原型对象的问题

原型模式省略了为构造函数传递初始参数的环节,结果所有实例在默认情况下都将取得相同的属性值。

原型中的所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,毕竟,通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,问题就比较突出了。文章来源地址https://www.toymoban.com/news/detail-840628.html

function Person(){}

Person.prototype = {
  name: 'Nicholas',
  age: 29,
  job: 'Software Engineer',
  friends: ['Shelby', 'Court'],
  sayName: function (){
    console.log(this.name);
  }
}

const person1 = new Person();
const person2 = new Person();

person1.friends.push('Van');

console.log(person1.friends);
// 'Shelby,Court,Van'
console.log(person2.friends);
// 'Shelby,COurt,Van'
console.log(person1.friends == person2.friends);
// true

到了这里,关于【编程向导】JavaScript-创建对象一期讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaScript系列从入门到精通系列第二十篇:使用工厂方法创建JavaScript对象,JavaScript构造函数详解,JavaScript类概念的介绍

    文章目录 一:使用工厂方法创建对象 1:原始写法 2:工厂方式 3:结果验证  二:构造函数 1:什么是构造函数 2:构造函数和普通函数的区别 3:构造函数的执行流程 三:类 1:什么是类 2:如何检查一个对象是否是个类的实例 3:Object的地位 四:构造函数修改 1:重大问题

    2024年02月08日
    浏览(40)
  • JavaScript 手写代码 第一期

    我们在日常开发过程中,往往都是取出来直接用,从来不思考代码的底层实现逻辑,但当我开始研究一些底层的东西的时候,才开始理解了JavaScript每个方法和函数的底层实现思路,我认为这可以很好的提高我们的代码水平和逻辑思维。 2.1.1 基本使用 定义 : 静态方法以一个现

    2024年02月10日
    浏览(48)
  • 【编程向导】代码管理-Git四期期讲解

    Git 最强大的就是其分支功能,但是如何分支才能更有效的提高开发效率,减少因为代码合并带来的问题,需要一个分支模型来规范,其实在 Git Flow 出现之前,已经有分支模型理论流程,当时是根据此理论,手动的按照规范操作分支,Git Flow 出现之后,将一部分操作流程简化

    2024年04月10日
    浏览(37)
  • leetcode — JavaScript专题(五):计数器 II、只允许一次函数调用、 创建 Hello World 函数、将对象数组转换为矩阵、节流、分块数组

    专栏声明:只求用最简单的,容易理解的方法通过,不求优化,不喜勿喷 题面 请你写一个函数 createCounter. 这个函数接收一个初始的整数值 init 并返回一个包含三个函数的对象。 这三个函数是: increment() 将当前值加 1 并返回。 decrement() 将当前值减 1 并返回。 reset() 将当前值

    2024年02月03日
    浏览(46)
  • 【JavaScript】讲解JavaScript的基础知识并且配有案例讲解

    🎊专栏【 前端易错合集】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 目录   🎁JavaScript嵌入网页的方式 🍔alert(\\\"这是一个JavaScript例子\\\");     (或者window.alert()) 🍔 document.write(\\\"这是一个Jav

    2024年02月08日
    浏览(53)
  • 【JavaScript速成之路】JavaScript内置对象--字符串对象

    📃个人主页:「小杨」的csdn博客 🔥系列专栏:【JavaScript速成之路】 🐳希望大家多多支持🥰一起进步呀! 📜前言:小杨在上一篇带着大家一起学习了JavaScript中的内置的数组对象,想必大家对JavaScript的内置的数组对象已经有所了解了,那么今天我们将继续带着大家学习一

    2023年04月10日
    浏览(44)
  • 【JavaScript】5.JavaScript内置对象

    JavaScript 中的对象分为3种 自定义对象 内置对象 浏览器对象 前面两种对象是JS 基础 内容,属于 ECMAScript; 第三个浏览器对象属于JS 独有的 内置对象就是指 JS 语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法) 内置对

    2023年04月20日
    浏览(41)
  • javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域。并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码,都存放在Window函数内(这是个假设),也就是说javascript中只有函数作用域(前面假设做前提下)

    2024年02月06日
    浏览(34)
  • javascript基础十:说说你对Javascript中this对象的理解

    一、定义 函数的 this 在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别 在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定) this 是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对

    2024年02月06日
    浏览(39)
  • JavaScript日期对象

    1.实例化 2.时间对象方法 3.时间戳 背景: 日期对象 目标 :掌握日期对象,可以让网页显示日期 日期对象 :用来表示时间的对象 作用 :可以得到当前系统时间 目标:能够实例化日期对象 1.在代码中发现了new时,一般将这个操作称为实例化 2.创建一个时间对象并获取

    2024年02月06日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包