文章较长,建议收藏以便浏览
《JavaScript高级程序设计(第三版)》学习总结
Object类型即对象类型,在ECMAScript中,对象其实就是一组数据和功能的集合。我们可以使用new操作符来创建:
let obj = new Object()
单单这一行代码,就可以引申出一系列疑问。创建的对象有什么属性和方法?new操作符干了啥?创建对象有哪些方式?既然是对象怎么实现继承?怎么实现对象深拷贝?and so on.如果一下子全都能说明白透彻的话,我斑愿称你为最强 。接下来我们挨个来剖析一下这些问题。
我们在控制台打印一下刚才声明的obj实例,得到以下结果,在这里就又得引入原型和原型链的概念,我就不做过多阐述了,可以参考JS中的原型和原型链(图解)。下面说一下里面的具体属性。
obj.hasOwnProperty("name")
)。以上是官方介绍Object的每个实例都具有的属性和方法,但是控制台打印的不仅仅只有这些,还有下面的,我们继续看:
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)
obj. __ proto __
obj. __ proto __ = 对象
let obj = {
}
__proto__
指向构造函数的prototype
,让空对象继承构造函数的原型obj.__proto__ = Object.prototype
Object.call(obj)
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")
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 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")
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() // 继承方法
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)
let Animal={...}
)class Animal {
constructor(){
this.ages = [1,2,3]
}
}
class Cat extends Animal{
constructor(){
super()
}
}
默认情况下对象之间的直接赋值都是浅拷贝,一个对象的属性如果是基本数据类型, 那么也都是深拷贝,如果对象的属性包含了引用数据类型, 才真正的区分深拷贝和浅拷贝。
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]
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的知识点,真是三天三夜都聊不完,就到这里吧。