js创建对象

394 阅读5分钟

文章主要对js创建对象的几种方式进行了简单的描述:

对象:无序属性的集合,其属性可以包含基本值、对象或者函数。

创建模式:

1、new Object:

方式:先创建空Object对象, 再动态添加属性/方法

使用场景:不确定对象内部数据

缺点:语句太多

   var person=new Object() 
       person.name="dsy"
       person.age=18
       

2、对象字面量

使用方式:使用{}创建对象, 同时指定属性/方法

缺点:如果创建多个对象, 有重复代码

var person = {
    name:"dsy",
    age:18,
    setInfo:function(){
        console.log(this.name)
    }
}

3、工厂模式

调用一个函数来解决需要创建多个相似属性对象的问题 缺点:没有一个具体的类型、都是Object类型

过程:创建一个对象 => 添加属性 => return出对象

function creayedPerson({name,age}){
    var person = new Object()
    person.name=name
    person.age = age
    person.setInfo = function(){
        console.log('this.name:',name)
    }
    return person
}
var per1 = creayedPerson({name:'dsy',age:18})
var per2 = creayedPerson({name:'lalal',age:20})

console.log('per1.constructor:',per1.constructor) // constructor标识对象类型是 Object
console.log('per1.constructor:',per1 instanceof Object) // true

4、构造函数

优点:可以将他的实例标示为一种特定类型

问题:相同的方法在不同实例都重新创建一遍,导致不同的作用域链和标识符解析,浪费内存

使用方法:

   1、函数名称大写字母开头
   2、使用new操作符来创建的新的实际例
 function Person({name,age}){
   this.name=name
   this.age = age
   this.setInfo = function(){
        console.log('this.name:',name)
    } 
    // this.setInfo = new Function("console.log('this.name',this.name)")
}
var per1 = new Person({name:'dsy',age:18})
var per2 = new Person({name:'lalal',age:20})

 console.log('per1.constructor:') // Person
 
 console.log(per1 instanceof Person) // true
 console.log(per1 instanceof Object) // true
 
 console.log(per2 instanceof Person) // true
 console.log(per2 instanceof Object) // true
 
 console.log(per1.setInfo == per2.setInfo) // false
 

new:1、创建一个新的对象 2、将构造函数的作用域赋予新的对象(this指向新对象) 3、执行构造函数的代码,给新对象添加属性 3、返回新对象

5、原型模式

优点:方法和属性所有实例共享

缺点:引用类型的属性一个实例改变,所有实例接收到的全部改变,没办法保存实例自有属性; 没办法初始化参数

  function Person(){}
  Person.prototype.name = 'dsy'
  Person.prototype.age = 18
  Person.setInfo=function(){
      console.log(this.name)
  }
  
  var per1 = new Person()
  var per2 = new Person()
  console.log('per1:',per1)
  console.log('原型对象:',Object.getPrototypeOf(per1)) // 返回实例指向构造函数的原型对象
  

prototype:通过调用构造函数而创建的那个对象实例的原型对象(函数的属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法)

原型对象:
       1、创建一个新函数,就会根据一组特定规则来为这个函数创建一个prototype的属性
       2、指向函数的原型对象
       3、默认情况下,原型对象会包含constructor属性,这个属性是一个指向prototype属性所在函数的指针
       4、调用构造函数实例之后,实例内部包含一个指针,指向构造函数的原型对象,浏览器实现_proto_(指针连接是指实例与构造函数的原型之间而不是与构造函数之间)

查找属性过程:实例属性=>原型对象属性 = >带来问题:实例中有同名属性和方法会只执行实例,切换了与原型对象属性之间的连接关系

重写原型:
    function Person(){}
    Person.prototype={
        name:'dsy',
        age:18,
        setInfo:function(){
            console.log(this.name)
        }
    }
    
    上面写法会导致构造函数的原型对象中constructor属性不再指向构造函数,指回来可以这样设定:
    Person.prototype={
        constructor:Person,
        name:'dsy',
        age:18,
        setInfo:function(){
            console.log(this.name)
        }
    }
原型动态性:
        function Person(){}
        var per1 = new Person()
        Person.name="dsy"
        Person.prototype.setInfo=function(){
            console.log('动态性1:')
            console.log(this.name) // dsy
        }
        per1.setInfo()
    
// console.log('动态性2:------------')
        function Person(){}
        var per1 = new Person()
        Person.prototype={
            constructor:Person,
            name:'dsy',
            age:18,
            setInfo:function(){
                console.log('动态性2:') // error,此时this指向还是最初的实例,没有name属性
                console.log(this.name)
            }
        }
        per1.setInfo()
        
    重写会切断实例与原型对象之间的关系
  

引用类型问题:

function Person(){}
Person.prototype.names=['a','b']
var pre1 = new Person()
var pre2 = new Person()
pre1.names.push('dsy')
console.log('pre1-name:',pre1.names) // ['a','b','dsy']
console.log('pre2-name:',pre2.names)  // ['a','b','dsy']
 

6、组合模式

定义实例属性+共享原型属性

每个实例都会有自己的一份实例属性的副本,但同时又共享着原型对象的引用,最大限度的节省了内存。另外,这种混合模式还支持向构造函数传递参数,可谓是集两种模式之长

 function Person({name,age}){
    this.age=age
    this.name = name
 }
 Person.prototype.setInfo = function(){
     console.log(this.name)
 }

7、动态原型

在构造函数中初始化原型(初次调用构造函数执行;所有实例中均可得到;)

 function Person({name,age}){
     this.age=age
     this.name = name
     if(typeof this.setInfo !='function'){
        Person.prototype.setInfo = function(){
         console.log(this.name)
        }
     }
 }

注意的点:不可使用对象字面量方式重写原型,会切断实例与原型之间的联系

8、寄生构造函数模式

 function Person({name,age}){
    var p = new Object()
    p.name = name
    p.age = age
    p.setInfo = function(){
        console.log(this.name)
       }
    return p
   }
   var pre1 = new Person({name:'dsy',age:18})

返回的对象与构造函数或者构造函数的原型属性之间没有关系(确定不了对象类型)

9、class

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类
class Person {
  bookNames="words" // 实例属性
  constructor(name, age) {
    this.name = name; // 实例属性
    this.age = age;
  }

  toString() { // 原型方法
    return 'name:' + this.name + ', this.age:' + this.age 
  }
}

constructor:构造方法

this:代表实例对象

toString:类的方法,不需要使用function关键字;方法之间不需要逗号分隔

1、原型继续存在
    class Person {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        
        toString() {
            return 'name:' + this.name + ', this.age:' + this.age 
        }
    }
    var pre1 = new Person('dsy',18)
    pre1.constructor === Person.prototype.constructor

在类的实例上面调用方法,其实就是调用原型上的方法(都是不可枚举,es5是可枚举):

在原型上添加多个方法:

Object.assign(Person.prototype,{
   a(){},
   b(){}
})
2、constructor:类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加
3、静态方法

如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用

静态方法包含this关键字,这个this指的是类,而不是实例

 class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  static toString() {
    return 'name:' + this.name + ', this.age:' + this.age 
  }
}
var pre1 = new Person('dsy',18)
pre1.toString() // error
Person.toString()

父类的静态方法,可以被子类继承

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"