对象

3700 字
19 分钟
对象

对象(Object)#

  • 对象是JS中的引用数据类型
  • 对象是一种复合数据类型,在对象中可以保存多个不同数据类型的属性
  • 使用typeof检查一个对象时,会返回object

对象的基本语法#

创建对象#

  • 方式一:

    var obj = new Object()
  • 方式二:

    var obj = {}
  • 方式三: 使用对象字面量,在创建对象时直接向对象中添加属性

    • 语法:

      var obj = {
      name: 'tom',
      age: 18,
      sex: '男',
      }

    注:在什么时候使用 对象["对象名"] 的方式

    1. 属性名包含特殊字符:_ 空格

      //需求: 添加一个属性: content-type: text/json
      //p.content-type = 'text/json' //不正确
      p['content-type'] = 'text/json'
    2. 属性名不确定

    //将name作为p的属性添加进p对象
    var prop = 'name'
    var value = 123
    // p.prop = value //不正确
    p[prop] = value
    ```
    1. 2个引用变量指向同一个对象, 通过一个引用变量修改对象内部数据, 另一个引用变量也看得见
    2. 2个引用变量指向同一个对象,让一个引用变量指向另一个对象, 另一个引用变量还是指向原来的对象

向对象中添加属性#

  • 语法: 对象.属性名 = 属性值 对象["属性名"] = 属性值
    • 对象的属性名没有任何要求,不需要遵守标识符的规范, 但是在开发中,尽量按照标识符的要求去写。
    • 属性值也可以任意的数据类型。

读取对象中的属性#

  • 语法:

    对象.属性名 对象["属性名"]

  • 如果读取一个对象中没有的属性,它不会报错,而是返回一个undefined

删除对象中的属性#

  • 语法: delete 对象.属性名 delete 对象["属性名"]

使用in检查对象中是否含有指定属性#

  • 语法: "属性名" in 对象

  • 如果在对象中含有该属性,则返回true如果没有则返回false

基本数据类型和引用数据类型#

  • 基本数据类型 String Number Boolean Null Undefined
  • 引用数据类型 Object
  • 基本数据类型的数据,变量是直接保存的它的值。 变量与变量之间是互相独立的,修改一个变量不会影响其他的变量。
  • 引用数据类型的数据,变量是保存的对象的引用(内存地址)。 如果多个变量指向的是同一个对象,此时修改一个变量的属性,会影响其他的变量。
  • 比较两个变量时,对于基本数据类型,比较的就是值, 对于引用数据类型比较的是地址,地址相同才相同

创建对象的方式#

Object构造函数模式#

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

  • 适用场景: 起始时不确定对象内部数据

  • 问题: 语句太多

    var p = new Object()
    p.name = 'Tom'
    p.age = 12
    p.setName = function (name) {
    this.name = name
    }

对象字面量模式#

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

  • 适用场景: 起始时对象内部数据是确定的

  • 问题: 如果创建多个对象, 有重复代码

    var p = {
    name: 'Tom',
    age: 23,
    setName: function (name) {
    this.name = name
    },
    }

工厂模式#

  • 套路: 通过工厂函数动态创建对象并返回

  • 适用场景: 需要创建多个对象

  • 问题: 对象没有一个具体的类型, 都是Object类型

    function createPerson(name, age) {
    var p = {
    name: name,
    age: age,
    setName: function (name) {
    this.name = name
    },
    }
    return p
    }
    var p1 = createPerson('Tom', 12)
    var p2 = createPerson('JAck', 13)

自定义构造函数模式#

  • 套路: 自定义构造函数, 通过new创建对象

  • 适用场景: 需要创建多个类型确定的对象

  • 问题: 每个对象都有相同的数据, 浪费内存

    function Person(name, age) {
    this.name = name
    this.age = age
    this.setName = function (name) {
    this.name = name
    }
    }
    var p1 = new Person('Tom', 12)
    var p2 = new Person('Tom2', 13)

构造函数+原型的组合模式#

  • 套路: 自定义构造函数, 属性在函数中初始化, 方法添加到原型上

  • 适用场景: 需要创建多个类型确定的对象

    function Person(name, age) {
    this.name = name
    this.age = age
    }
    Person.prototype.setName = function (name) {
    this.name = name
    }
    var p1 = new Person('Tom', 12)
    var p2 = new Person('JAck', 23)

原型 prototype#

基本介绍#

我们所创建的每一个函数,解析器都会向函数中添加一个属性prototype,这个属性对应着一个对象,这个对象就是我们所谓的原型对象 如果函数作为普通函数调用prototype没有任何作用,当函数以构造函数的形式调用时,它所创建的对象中都会有一个隐含的属性,指向该构造函数的原型对象,我们可以通过 __proto__ 来访问该属性

原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中。

当我们访问对象的一个属性或方法时,它会先在对象自身中寻找,如果有则直接使用,如果没有则会去原型对象中寻找,如果找到则直接使用

我们创建构造函数时,可以将这些对象共有的属性和方法,统一添加到构造函数的原型对象中,这样不用分别为每一个对象添加,也不会影响到全局作用域,就可以使每个对象都具有这些属性和方法了

  • 向构造函数的原型中添加属性

    MyClass.prototype.name = 123
  • 向构造函数中原型中添加一个方法

    MyClass.prototype.sayHello = function(){
    console.log("hello")
    }

hasOwnProperty()#

  • 可以使用对象的hasOwnProperty()来检查对象自身中是否含有该属性
  • 使用hasOwnProperty()方法只有当对象自身中含有属性时,才会返回true
  • 使用in检查对象中是否含有某个属性时,如果对象中没有但是原型中有,也会返回true
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
Person.prototype.a = 123
var p = new Person('张三', 18, '男')
console.log('a' in p) //true
console.log(p.hasOwnProperty('a')) //false
console.log(p.__proto__.hasOwnProterty('a')) //true
  • 注意:

    • 原型对象也是对象,所以它也有原型,当我们使用一个对象的属性或方法时,会现在自身中寻找,自身中如果有,则直接使用,如果没有则去原型对象中寻找,如果原型对象中有,则使用,如果没有则去原型的原型中寻找,直到找到Object对象的原型,Object对象的原型没有原型,如果在Object原型中依然没有找到,则返回undefined

    • 通过原型对象添加的属性和方法函数本身是没有的,这就是静态方法和实例方法的区别

  • 总结

    • 函数的prototype属性: 在定义函数时自动添加的, 默认值是一个空Object对象
    • 对象的__proto__属性: 创建对象时自动添加的, 默认值为构造函数的prototype属性值
    • 实例与构造函数原型有直接联系,但是实例与构造函数之间没有。
    • 程序员能直接操作显式原型, 但不能直接操作隐式原型(ES6之前)

原型链#

  • 每个构造函数都有一个原型对象,原型有一个属性(constructor)指回构造函数,而实例有一个内部指针(__proto__)指向原型,又原型也是另一个类型的实例,那就意味着这个原型本身有一个内部指针指向另一个原型,相应的另一个原型也有一个指针指向另一个构造函数。这样实例和原型之间就构造了一条原型链。
  • 任何函数的默认原型都是一个Object的实例,因此构造函数的原型对象都有__proto__指向Object.prototype(包括Function),而Object.__proto__ === null,因此Object.__proto__是整条原型链的末端。

注:

  • 实例对象的__proto__等于构造函数的prototype
  • 实例对象的prototype是undefined
  • 任何函数的构造函数都是Function,包括Function也不例外,因此Function.__proto__ === Function.prototype
  • function是Function的实例,实例化的对象是Object的实例
var Person = function () {
name: '123'
}
Person.prototype.name = 'aaa'
var p1 = new Person()
console.log(p1.name) //aaa
console.log(Function.prototype === Object.__proto__) //true (Object是Function的实例对象
console.log(Function.__proto__ === Function.prototype) //true
console.log(Object.prototype.prototype) //undefined
console.log(Function.prototype.__proto__ === Object.prototype) //true
console.log(Person.__proto__ === Function.prototype) //true
console.log(Person.prototype.__proto__ === Object.prototype) //true
console.log(Object.__proto__ === Function.prototype) //true

原型链1
原型链1

  • 重写函数的prototype后,相当于prototype指向了一个新的对象
var Person = function () {
name: '123'
}
Person.prototype.name = 'aaa'
var p1 = new Person()
Person.prototype = {
name: 'bbb',
age: 5,
}
var p2 = new Person()
console.log(p1.constructor) //Person
console.log(Person.constructor) //Function
console.log(p1.age) //undefined
console.log(p1.name) //aaa
console.log(p1.__proto__.constructor.prototype.age) //5
console.log(p2.age) //5
console.log(p1.name) //bbb

原型链2
原型链2

继承#

通过原型链继承#

  • 套路

    1. 定义父类型构造函数
    2. 给父类型的原型添加方法
    3. 定义子类型的构造函数
    4. 创建父类型的对象赋值给子类型的原型
    5. 将子类型原型的构造属性设置为子类型
    6. 给子类型原型添加方法
    7. 创建子类型的对象: 可以调用父类型的方法
  • 关键

    子类型的原型为父类型的一个实例对象

    function SuperType() { this.colors = [‘red’, ‘blue’, ‘green’] this.name = 2 }

    function SubType() {}

    // 继承 SuperType SubType.prototype = new SuperType()

    let instance1 = new SubType() instance1.name += 2 instance1.colors.push(‘black’) console.log(instance1.colors) // “red,blue,green,black” console.log(instance1.name) // 4

    let instance2 = new SubType() console.log(instance2.colors) // “red,blue,green,black” console.log(instance2.name) // 2

在这个例子中, SuperType 构造函数定义了一个 colors 属性,其中包含一个数组(引用值)。每个 SuperType 的实例都会有自己的 colors 属性,包含自己的数组。但是,当 SubType 通过原型继承SuperType 后, SubType.prototype 变成了 SuperType 的一个实例,因而也获得了自己的 colors属性。这类似于创建了 SubType.prototype.colors 属性。最终结果是, SubType 的所有实例都会共享这个 colors 属性。

导致SubType的每个实例引用 colors 属性, 都会从原型链中寻找SubType.prototype.colors 属性,相当于每个SubType都共用同一个 colors 属性。

而对于基本数据类型来说,执行instance1.name += 2 这行代码,会在instance1 实例中添加name属性,然后在从SubType.prototype中读取name属性的值(2),最后 +2,等价于instance1.name = SubType.prototype.name + 2。而SubType.prototype.name的值没有改变,仅读取instance2.name的值时,instance2会从原型链中读取SubType.prototype.name的值(2)

盗用构造函数继承#

  • 套路:
    1. 定义父类型构造函数
    2. 定义子类型构造函数
    3. 在子类型构造函数中调用父类型构造
  • 关键:
    1. 在子类型构造函数中通用call() 调用父类型构造函数
function SuperType(name) {
this.name = name
}
function SubType() {
// 继承 SuperType 并传参
SuperType.call(this, 'Nicholas') // this.SuperType("Nicholas") 等价于 this.name = name
// 实例属性
this.age = 29
}
let instance = new SubType()
console.log(instance.name) // "Nicholas";
console.log(instance.age) // 29

盗用构造函数的一个优点就是可以在子类构造函数中向父类构造函数传参。 在 SubType 构造函数中调用 SuperType 构造函数时传入这个参数,实际上会在 SubType 的实例上定义 name 属性。

组合继承#

  • 原型链+盗用构造函数的组合继承
    • 使用原型链继承原型上的属性和方法
    • 通过盗用构造函数继承实例属性
function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, age) {
// 继承属性
SuperType.call(this, name)
this.age = age
}
// 继承方法
SubType.prototype = new SuperType()
SubType.prototype.sayAge = function () {
console.log(this.age)
}
let instance1 = new SubType('Nicholas', 29)
instance1.colors.push('black')
console.log(instance1.colors) // "red,blue,green,black"
instance1.sayName() // "Nicholas";
instance1.sayAge() // 29
let instance2 = new SubType('Greg', 27)
console.log(instance2.colors) // "red,blue,green"
instance2.sayName() // "Greg"
instance2.sayAge() // 27

在这个例子中, SuperType 构造函数定义了两个属性, name 和 colors,而它的原型上也定义了一个方法叫 sayName()。 SubType 构造函数调用了 SuperType 构造函数,传入了 name 参数,然后又定义了自己的属性 age。此外, SubType.prototype 也被赋值为 SuperType 的实例。原型赋值之后,又在这个原型上添加了新方法 sayAge()。 这样,就可以创建两个 SubType 实例,让这两个实例都有自己的属性,包括 colors,同时还共享相同的方法。

组合继承
组合继承

Date对象#

  • 在JS中使用Date对象来表示一个时间

  • 创建一个Date对象

    • 如果直接使用构造函数创建一个Date对象,则会封装为当前代码执行的时间

      var date = new Date(); //封装此行代码执行的时间
      console.log(date); //打印date Fri Dec 11 2020 23:32:17 GMT+0800 (中国标准时间)
  • 创建一个指定的时间对象

    • 需要在构造函数中传递一个表示时间的字符串作为参数

    • 日期的格式: 月/日/年 时:分<秒>

      var date = new Date("2/18/2011 11:10:30");
      console.log(date); //2011-02-18T03:10:30.000Z

getDate()#

  • 获取当前日期对象是几日

    var date = new Date("2/18/2011 11:10:30");
    var d = date.getDate();
    console.log(d); //18

getDay()#

  • 获取当前日期对象时周几

  • 会返回一个0-6的值 0 表示周日 1 表示周一

    var date = new Date("2/18/2011 11:10:30")
    var d = date.getDay()
    console.log(d) //5 (周六)

getMonth()#

  • 获取当前时间对象的月份

  • 会返回一个0-11的值 0 表示1月 1 表示2月 11 表示12月

    var date = new Date("2/18/2011 11:10:30");
    var d = date.getMonth();
    console.log(d); //1

​

getFullYear()#

  • 获取当前日期对象的年份

    var date = new Date("2/18/2011 11:10:30");
    var d = date.getFullYear();
    console.log(d); //2011

getTime()#

  • 获取当前日期对象的时间戳
    • 时间戳,指的是从格林威治标准时间的1970年1月1日,0时0分0秒 到当前日期所花费的毫秒数
  • 计算机底层在保存时间时使用都是时间戳
    var date = new Date("2/18/2011 11:10:30")
    var time = date.getTime()
    console.log(time) //1297998630000 (毫秒数)

Date.now()#

  • 获取当前的时间戳

    var time = Date.now();
  • 可以利用时间戳来测试代码的执行的性能

Math对象#

Math和其他的对象不同,它不是一个构造函数,它属于一个工具类不用创建对象,它里边封装了数学运算相关的属性和方法

abs()#

可以用来计算一个数的绝对值

console.log(Math.abs(-2)) //2

Math.ceil()#

可以对一个数进行向上取整,小数位只有有值就自动进1

console.log(Math.ceil(1.1)) //2

Math.floor()#

可以对一个数进行向下取整,小数部分会被舍掉

console.log(Math.floor(1.99)) //1

Math.round()#

可以对一个数进行四舍五入取整

console.log(Math.round(1.4)) //1

Math.random()#

可以用来生成一个0-1之间的随机数

//生成一个0-10的随机数
Math.round(Math.random() * 10)
//生成一个1-5之间的随机数
Math.round(Math.random() * 5 + 1)

max()#

可以获取多个数中的最大值

var max = Math.max(10,45,30,100) // 100

min()#

可以获取多个数中的最小值

var min = Math.min(10,45,30,100) // 10

Math.pow(x,y)#

返回x的y次幂

console.log(Math.pow(12,3)) //12^3

Math.sqrt()#

用于对一个数进行开方运算

console.log(Math.sqrt(20))

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

对象
https://sukksa.github.io/posts/dui-xiang/
作者
Sukksa
发布于
2021-10-18
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Sukksa
记录前端学习与日常
公告
博客刚刚上线,内容还在慢慢补齐。欢迎随便逛逛~
分类
标签
最新动态
站点统计
文章
69
分类
16
标签
17
总字数
178,517
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录