JavaScript高级程序设计--数据类型(2)_Object

文章较长,建议收藏以便浏览

《JavaScript高级程序设计(第三版)》学习总结

Object

  Object类型即对象类型,在ECMAScript中,对象其实就是一组数据和功能的集合。我们可以使用new操作符来创建:

let obj = new Object()

单单这一行代码,就可以引申出一系列疑问。创建的对象有什么属性和方法?new操作符干了啥?创建对象有哪些方式?既然是对象怎么实现继承?怎么实现对象深拷贝?and so on.如果一下子全都能说明白透彻的话,我斑愿称你为最强 。接下来我们挨个来剖析一下这些问题。

创建的对象有什么属性和方法?

我们在控制台打印一下刚才声明的obj实例,得到以下结果,在这里就又得引入原型和原型链的概念,我就不做过多阐述了,可以参考JS中的原型和原型链(图解)。下面说一下里面的具体属性。
在这里插入图片描述

  • constructor:保存着用于创建当前对象的函数。在这里及是Object()。
  • hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中是否存在。其中,作为参数的属性名必须以字符串的形式指定(eg: obj.hasOwnProperty("name"))。
  • isPrototypeOf(object):用于检查传入的对象是否是另一个对象的原型。
  • propertyIsEnumerable(propertyName):用于检查给定的属性在当前对象实例中是否能够使用for-in来枚举出来,可以用来区分实例属性和原型属性。
  • toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应。
  • toString():返回对象的字符串表示。
  • valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同。JS中toString()、toLocaleString()、valueOf()的区别

以上是官方介绍Object的每个实例都具有的属性和方法,但是控制台打印的不仅仅只有这些,还有下面的,我们继续看:

  • _ _defineGetter _ _:给对象添加getter方法
  • _ _defineSetter _ _:给对象添加setter方法
  • _ _lookupGetter _ _:返回getter的定义函数
  • _ _lookupSetter _ _:返回setter的定义函数
let obj =  new Object()
// name添加getter
obj.__defineGetter__('name', ()=>this.name)
console.log(obj.name) // null
obj.name = "luffy"
// 此时name还没有setter方法,因此不能赋值
console.log(obj.name) // null
// name添加setter
obj.__defineSetter__('name',(name)=>(this.name = name))
obj.name = "luffy"
// 此时可以看到name赋值成功
console.log(obj.name) // luffy
// 返回相应的定义函数
console.log(obj.__lookupGetter__('name')) // ()=>this.name
console.log(obj.__lookupSetter__('name')) // (name)=>(this.name = name)
  • get _ _ proto _ _:获取_ _proto _ _指向,用法就是obj. __ proto __
  • set _ _ proto _ _:设置_ _proto _ _指向,用法就是obj. __ proto __ = 对象
new操作符干了啥?
  1. 创建一个空对象
    let obj = {}
    
  2. 将空对象的__proto__指向构造函数的prototype,让空对象继承构造函数的原型
    obj.__proto__ = Object.prototype
    
  3. 将构造函数的this指向新对象,并执行构造函数为新对象添加属性
    Object.call(obj)
    
  4. 返回新对象
    return obj
    
创建对象有哪些方式?

创建单个对象的话采用以下两个方式即可:

  • new操作符+Object构造函数

    let obj = new Object()
    obj.name = "luffy"
    
  • 对象字面量

    let obj = {
    	name: "luffy"
    }
    

但是平常敲代码的业务中,往往需要创建很多对象,如果使用以上两种方法的话,会产生大量重复代码,产生代码冗余问题,因此以下几种方法我们也需要掌握:

  • 工厂模式:用函数来封装以特定的接口创建对象的细节
    • 缺点:没有解决对象识别的问题(即没办法知道一个对象的类型)
function createObj(name){
	let obj = new Object()
	obj.name = name
	obj.sayName = () =>{
		console.log(this.name)
	}
	return obj
}
let obj = createObj("luffy")
  • 构造函数模式
    • 和工厂模式的不同之处:没有显示地创建对象;直接将属性和方法赋给了this对象;没有return语句。
    • 缺点:每个方法都要在每个实例上重新创建一遍,即下面的sayName(),可以通过把函数定义转移到构造函数外部来解决这个问题。
function ObjFn(name){
	this.name = name
	this.sayName = ()=>{
		console.log(this.name)
	}
}
let obj = new ObjFn("luffy")
//不通过new调用的话,它就是一个普通函数,
//那么执行的属性和方法在浏览器中就会指向window对象,
//就能得到window.sayName()的结果为"who"
ObjFn("who") 

// 优化版
function ObjFn(name){
	this.name = name
	this.sayName = sayName
}
const sayName = ()=>{
	console.log(this.name)
}
  • class语法糖(需要与时俱进不是(っ•̀ω•́)っ✎⁾⁾ )
class Obj { // 注意命名规范
	constructor(name){
		this.name = name
	}
	sayName(){
		console.log(this.name)
	}
}
let obj = new Obj("luffy")
  • 原型模式
    • 优点:不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到对象原型中。
// 初始写法
function Obj() {
	Obj.prototype.name = "luffy"
	Obj.prototype.sayName = () =>{
		console.log(this.name)
	}
}
let obj1 = new Obj()
let obj2 = new Obj()
obj1.name = "Tom" // 不会修改原型对象的name属性,而是实例对象添加了一个同名属性
obj1.sayName() // Tom,访问实例对象的name属性,屏蔽原型对象中的同名属性
obj2.sayName() // luffy,访问原型对象的name属性
// 进阶写法
function Obj(){}
Obj.prototype = {
	// constructor: Obj, // 设置constructor属性的指向
	name: "luffy",
	sayName: ()=>{
		console.log(this.name)
	}
}
  • 组合使用构造函数模式和原型模式
    • 构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。最大限度节省了内存,还支持向构造函数传递参数。
function Obj(name){
	this.name = name
}
Obj.prototype = {
	constructor: Obj,
	sayName: ()=>{
		console.log(this.name)
	}
}
let obj = new Obj("luffy")
  • 寄生构造函数模式
    • 就是相当于使用new来调用了工厂模式,返回的对象与构造函数以及构造函数的原型属性之间没有关系,酌情使用!
function createObj(name){
	let obj = new Object()
	obj.name = name
	obj.sayName = () =>{
		console.log(this.name)
	}
	return obj
}
let obj = new createObj("luffy")
  • 稳妥构造函数模式
    • 只能调用构造函数内的方法去访问其数据成员。
function createObj(name){
	let obj = new Object()
	obj.sayName = () =>{
		console.log(name)
	}
	return obj
}
let obj = new createObj("luffy")
obj.sayName() // luffy
既然是对象怎么实现继承?
  • 原型链
    • 缺点:包含引用类型值的原型会被所有实例共享;在创建子类型的实例时,不能向超类型的构造函数中传递参数。
function Animal(){
    this.ages = [1,2,3]
}
function Cat(){
}
Cat.prototype = new Animal()
let cat1 = new Cat()
let cat2 = new Cat()
console.log(cat1.ages) //[ 1, 2, 3 ]
console.log(cat2.ages)//[ 1, 2, 3 ]
cat1.age.push(4) // 修改cat1会影响cat2
console.log(cat1.ages)//[ 1, 2, 3, 4 ]
console.log(cat2.ages)//[ 1, 2, 3, 4 ]
  • 借用构造函数
    • 缺点:函数复用问题
function Animal(name){
	this.name = name
    this.ages = [1,2,3]
}
function Cat(){
	Cat.call(this, "yoyo")
}
  • 组合继承
    • 缺点:无论什么时候都会调用两次超类型的构造函数,一次是创建子类型原型的时候,一次是在构造函数内部。
function Animal(name){
	this.name = name
    this.ages = [1,2,3]
    this.sayName = () =>{
		console.log(this.name)
	}
}
function Cat(){
	Cat.call(this, "yoyo") // 继承属性
}
Cat.prototype = new Animal() // 继承方法
  • 原型式继承
    • ES5新增的Object.create()方法规范化了原型式继承。
let Obj = {
	name: "luffy"
} 
let obj1 = Object.create(Obj) // 一个参数
let obj2 = Object.create(Obj,{age:{value: 23}}) // 两个参数
  • 寄生式继承
let Obj = {
	name: "luffy"
} 
function createObj(origin){
	let clone = new Object(origin)
	clone.sayName = (name) =>{ // 可以增强对象
		console.log(name)
    }
    return clone
}
let obj = createObj(Obj)
obj.sayName(obj.name) // luffy
  • 寄生组合式继承
function inheritPrototype(subType,superType){
	let prototype1 = new Object(superType.prototype) // 创建对象
	prototype1.constructor = subType // 增强对象
	subType.prototype = prototype1 // 绑定对象
}
function Animal(name){
	this.name = name
    this.ages = [1,2,3]
}
Animal.prototype.sayName = () =>{
	console.log(this.name)
}
function Cat(){
	Cat.call(this, "yoyo") // 继承属性
}
inheritPrototype(Cat, Animal)
  • class对应的extends继承
    • 不可继承常规对象(eg: let Animal={...})
class Animal {
	constructor(){
		this.ages = [1,2,3]
	}
}
class Cat extends Animal{
	constructor(){
		super()
	}
}
怎么实现对象深拷贝?

默认情况下对象之间的直接赋值都是浅拷贝,一个对象的属性如果是基本数据类型, 那么也都是深拷贝,如果对象的属性包含了引用数据类型, 才真正的区分深拷贝和浅拷贝。

  • 对象的拷贝:将一个对象赋值给另外一个对象
  • 浅拷贝:将A对象赋值给B对象,修改B对象的属性和方法会影响到A对象的属性和方法
let obj = {a:1,b:2,c:[1,2]}
let obj1 = {}
for(let key in obj){
    obj1[key] = obj[key]
} // 等同于 Object.assign(obj1,obj)
obj1.c.push(3);
console.log(obj.c) // [1,2,3]
  • 深拷贝:将A对象赋值给B对象,修改B对象的属性和方法不会影响到A对象的属性和方法(附上一个大佬实现的包含所有类型的深拷贝方法)
const deepClone = (obj, hash = new WeakMap())=> {
    if (obj === null) return obj
    if (obj instanceof Date) return new Date(obj)
    if (obj instanceof RegExp) return new RegExp(obj)
    if (typeof obj !== 'object') return obj
    if (hash.get(obj)) return hash.get(obj)
    let cloneObj = new obj.constructor()
    hash.set(obj, cloneObj)
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        cloneObj[key] = this.deepClone(obj[key], hash)
      }
    }
    return cloneObj
}

关于Object的知识点,真是三天三夜都聊不完,就到这里吧。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值