JavaScript中的对象(上)

简介: JavaScript中的对象(上)

js中的对象(上)

基础知识

对象是包括属性与方法的数据类型,JS 中大部分类型都是对象如 String/Number/Math/RegExp/Date 等等。

传统的函数编程会有错中复杂的依赖很容易创造意大利式面条代码。

所以我们使用面向对象的编程思想,将方法封装到对象中,同时也方便取值。

OOP

  • 对象是属性和方法的集合即封装
  • 将复杂功能隐藏在内部,只开放给外部少量方法,更改对象内部的复杂逻辑不会对外部调用造成影响即抽象
  • 继承是通过代码复用减少冗余代码
  • 根据不同形态的对象产生不同结果即多态

基本声明

使用字面量形式声明对象是最简单的方式

在对象中的函数称为方法,我们可以这样声明func: function(){},也可以这样简写func(){}

let obj = {
  age: 18,
  fucn(){
    console.log(this.age);//不需要传值,直接使用this取值
  }
};
obj.fucn();//18

其实字面量形式在系统内部也是使用构造函数 new Object创建的,后面会详细介绍构造函数。

let hd = {};
let houdunren = new Object();
console.log(hd, houdunren);
console.log(hd.constructor);
console.log(houdunren.constructor);

操作属性

使用点语法.获取,或者使用中括号[]获取。

let obj = {
  age: 18,
  'user name': 'xx'
};
console.log(obj.age);//18
console.log(obj['age']);//18

可以看出使用.操作属性更简洁,[]主要用于通过变量定义属性的场景

let obj = {
  age: 18,
};
let dd = 'age';
console.log(obj.dd);//undefined
console.log(obj[dd]);//18

如果属性名不是合法变量名就必须使用扩号的形式了,使用点语法会报错。

let obj = {
  'user name': 'xx',
  'uu-dd': 123
};
console.log(obj['user name']);//xx
console.log(obj['uu-dd']);123

对象属性的添加删除

对象和方法的属性可以动态的添加或删除。

let obj = {
  name: 'tt'
};

obj.age = 19;
console.log(obj.age);//19
obj['dd'] = '这是dd';
console.log(obj.dd);//这是dd

obj.func = function(){
  console.log('this is a func');
}
obj.func();//this is a func

console.log(obj.name);//tt
delete obj.name;
console.log(obj.name);//undefined

函数的传参(引用/值?)

当给函数传递参数时,如果传递的是值类型,就是传值。如果传递的是引用类型,就是传递地址。

let n = 1;
let user= {};
function funcN(a){
  a += 10; 
  console.log(a);
}
//传值,两个变量不同内存地址
funcN(n);//11
console.log(n);//1

function funcObj(obj){
  obj.name = 'xiaoxiao';
  console.log(obj);
}
//传址,两个变量同一内存地址
funcObj(user);//{ name: 'xiaoxiao' }
console.log(user);//{ name: 'xiaoxiao' }

展开语法...在对象中的使用

使用...可以展示对象的结构,下面是实现对象合并的示例

let obj = {
  name: 'xx',
  age: 20
}
newObj = {...obj};
console.log(newObj);//{ name: 'xx', age: 20 }

newObj1 = {...obj,love: 'js'};
console.log(newObj1);//{ name: 'xx', age: 20, love: 'js' }

我们可以用来进行函数参数合并

let obj = {
  type: '弱类型',
  name: 'js'
}

newObj = {...obj, type: '强类型', name: 'ts'};
console.log(newObj);//{ type: '强类型', name: 'ts' }

解构赋值

解构语法在对象中的使用

es6中,我们可以使用解构语法来对变量进行赋值

let obj = {
  type: '弱类型',
  name: 'js'
}

let {type: a, name: b} = obj;
console.log(a,b);//弱类型 js

let {type: type, name: name} = obj;
console.log(type, name);//弱类型 js

如果需要赋值的变量与对象的属性名一致,我们可以简写

let obj = {
  type: '弱类型',
  name: 'js'
}
let {type, name} = obj;
console.log(type,name);//弱类型 js

当然,如果函数的返回值是一个对象,我们也可以用解构赋值来赋值。因为func()同样是一个对象。

function func(){
  return {
    type: '弱类型',
    name: 'js'
  }
}
let {type, name} = func();
console.log(type,name);//弱类型 js

我们也可以用解构赋值来想函数传递参数,这通常应用于后端传过来的参数(是一个对象)。

function func({name,age}){
  console.log(name,age);
}

func({name: 'xiaoxiao', age: 20});//xiaoxiao 20

当然,我们也不一定要将对象中的全部属性对外面的属性赋值。

console.log(Math);//Math对象
let {random} = Math;
console.log(random());//0.8685842409329827

严格模式

非严格模式可以不使用声明指令,严格模式下必须使用声明。所以建议使用 let 等声明。

下面的代码在非严格模式下是可以使用的,但在严格模式下不能进行。

let obj = {
  name: 'xiao',
  age: 20
};
({name,age} = obj);
console.log(name, age);//xiao 20

所以我们一般使用let/const声明语句声明

"use strict";
let obj = {
  name: 'xiao',
  age: 20
};
let {name,age} = obj;
console.log(name, age);//xiao 20

简洁定义

如果属性名与赋值的变量名相同可以更简洁

let obj = {
  name: 'xiao',
  age: 20
};
let {name,age} = obj;
console.log(name,age);//xiao 20

只赋值部分变量,在数组的解构赋值中,我们要取到中间的值需要用逗号隔开,而对象的解构赋值我们可以直接取。

let arr = [1,2,3];
let [,b] = arr;
console.log(b);//2

let obj = {
  name: 'xiao',
  age: 20
};
let {age} = obj;
console.log(age);//20

可以直接使用变量赋值对象属性

let a = '我是a', b = '我是b';
console.log({a,b});//{ a: '我是a', b: '我是b' }

多层对象的解构赋值操作

let obj = {
  name: 'obj',
  lesson: {
    title: 'houdunren'
  }
}
let {name, lesson:{title}} = obj;
console.log(name,title);//obj houdunren

解构的默认值配置

在数组的解构赋值中,我们可以这样设置默认值

let arr = [1,2];
let [a,b,c=3] = arr;
console.log(a,b,c);//1 2 3

同样的在对象中,我们也可以这样操作

let obj = {
  name: 'xiao',
  age: 20
}
let {age,name,like='js'} = obj;
console.log(name,age,like);//xiao 20 js

我们也可以在函数中这样使用

function theObj(obj = {}){
  let {title='js',click=88,author='xj'} = obj;
  console.log(title,click,author);
}
theObj();//js 88 xj
theObj({title:'ts',click:50});//ts 50 xj

函数参数

function func(demo,{title, detail:{click, author}}){
  console.log(demo,title, click, author);
}
func('这是demo',{title: 'js', detail:{click: 88, author: 'xj'}});//这是demo js 88 xj

检测属性(涉及部分原型链知识)

hasOwnProperty检测对象自身是否包含指定的属性,不检测原型链上继承的属性。

let arr = [1,2];
console.log(arr.hasOwnProperty('length'));//true
console.log(arr.hasOwnProperty('concat'));//false

使用 in 可以在原型对象上检测

 let arr = [1,2];
console.log('concat' in arr);//true

我们可以设置某个对象的原型对象

<body>

</body>
<script>
  let obj = {
    func(){
      return 123;
    }
  };
  let objSon = {};
  Object.setPrototypeOf(objSon,obj);
  console.log(objSon);//可以看到他的原型对象为obj
  console.log(objSon.func());//123
  console.log(objSon.hasOwnProperty('func'));//false
  console.log('func' in objSon);//true
</script>

对此,可以用来进行一传递些对象参数的应用

<body>

</body>
<script>
  let func = function(options){
    if(!options.hasOwnProperty('url'))  {
      throw new Error('没有设置url');
    }
  }
  func({user: 'kk'});//Uncaught Error: 没有设置url
</script>

assign

以往我们使用类似jQuery.extend 等方法设置属性,现在可以使用 Object.assign 静态方法

从一个或多个对象复制属性(浅拷贝)

// 使用Object.assign方法(浅拷贝)
let hd = { a: 1, b: 2 };
Object.assign(hd, { f: 1 }, { m: 9 });
console.log(hd);//{ a: 1, b: 2, f: 1, m: 9 }

JSON.stringify

JSON.stringify可将值转换为JSON字符串

<body>

</body>
<script>
  let lesson = [
    {
      title: 'js',
      category: '弱类型'
    },{
      title: 'ts',
      category: '强类型'
    },{
      title: 'golang',
      category: '强类型'
    }
  ];
  let res = lesson.reduce((obj,cur,index)=>{
    obj[`${cur['category']}-${index+1}`] = cur;
    return obj;
  },{});
  console.log(JSON.stringify(res,null,2));
</script>

遍历对象

获取内容

使用系统提供的 API 可以方便获取对象属性与值

let lesson = {
  title: 'js',
  click: 88
};
console.log(Object.values(lesson));//[ 'js', 88 ]
console.log(Object.keys(lesson));//[ 'title', 'click' ]
console.log(Object.entries(lesson));//[ [ 'title', 'js' ], [ 'click', 88 ] ]

for/in

使用for/in遍历对象属性

let lesson = {
  title: 'js',
  click: 88
};
for(let keys in lesson){
  console.log(keys,lesson[keys]);
}
// title js
// click 88

for/of

for/of用于遍历迭代对象,不能直接操作对象。但Object对象的keys/方法返回的是迭代对象。

let lesson = {
  title: 'js',
  click: 88
};
for(const keys of Object.keys(lesson)){
  console.log(keys);
}
// title
// click

获取所有对象属性

let lesson = {
  title: 'js',
  click: 88
};
for(const values of Object.values(lesson)){
  console.log(values);
}
// js
// 88

同时获取属性名与值

let lesson = {
  title: 'js',
  click: 88
};
for(const [keys,values] of Object.entries(lesson)){
  console.log(keys,values);
}
// title js
// click 88

遍历添加元素 DOM 练习

<body>

</body>
<script>
  let lessons = [
    { name: "js", click: 23 },
    { name: "ts", click: 12 }
  ];
  let ul = document.createElement('ul');
  for (const value of lessons) {
    let li = document.createElement('li');
    li.innerHTML = `课程${value.name}  ${value.click}`;
    ul.appendChild(li);
  }
  document.body.appendChild(ul);
</script>

对象拷贝

对象赋值时复制的内存地址,所以一个对象的改变直接影响另一个

let obj = {
  name: '后盾人',
  user: {
      name: 'hdcms'
  }
}
let a = obj;
let b = obj;
b.name = 'lisi';
console.log(a.name); //lisi

浅拷贝

使用for/in执行对象拷贝

let obj = {
  name: 'xiaoxiao',
  user: {
      name: 'hdcms'
  }
};
let newObj = {};
for (const key in obj) {
  newObj[key] = obj[key];
}
obj.name = 'xj';
obj.user = {};
console.log(newObj);//{ name: 'xiaoxiao', user: { name: 'hdcms' } }
console.log(obj);//{ name: 'xj', user: {} }

Object.assign 函数可简单的实现浅拷贝,它是将两个对象的属性叠加后面对象属性会覆盖前面对象同名属性。

let obj = {
  name: 'xiaoxiao',
  user: {
      name: 'hdcms'
  }
};
let newObj = {};
Object.assign(newObj,obj)
obj.name = 'xj';
obj.user = {};
console.log(newObj);//{ name: 'xiaoxiao', user: { name: 'hdcms' } }
console.log(obj);//{ name: 'xj', user: {} }

使用展开语法也可以实现浅拷贝

let obj = {
  name: 'xiaoxiao',
  user: {
      name: 'hdcms'
  }
};
let newObj = {...obj};
obj.name = 'xj';
obj.user = {};
console.log(newObj);//{ name: 'xiaoxiao', user: { name: 'hdcms' } }
console.log(obj);//{ name: 'xj', user: {} }

深拷贝

浅拷贝不会将深层的数据复制,例如:

let obj = {
  name: 'xiaoxiao',
  user: {
      name: 'hdcms'
  }
};
let newObj = {...obj};
obj.name = 'xj';
obj.user.name = 'mmm';
console.log(newObj);//{ name: 'xiaoxiao', user: { name: 'mmm' } }
console.log(obj);//{ name: 'xj', user: { name: 'mmm' } }

我们可以使用函数来进行实现

let demoObj = {
  name: 'xiaoxiao',
  user: {
      name: 'hdcms'
  },
  arr: []
};

let supCopy = function(object){
  //因为数组与对象的typeof的值都为object,所以需要用instanceof来区分(不然数组也会变成对象)
  let obj = object instanceof Array ? [] : {};
  for (const [k,v] of Object.entries(object)) {
    obj[k] = typeof v === 'object' ? supCopy(v) : v;//如果是引用类型,就再次调用函数,参数为值
  }
  return obj;
}

let newObj = supCopy(demoObj);
demoObj.arr.push('xj');
demoObj.user.name = 'ddwb';
console.log(newObj);//{ name: 'xiaoxiao', user: { name: 'hdcms' }, arr: [] }
console.log(demoObj);//{ name: 'xiaoxiao', user: { name: 'ddwb' }, arr: [ 'xj' ] }

使用函数创建对象

对象可以通过内置或自定义的构造函数创建。

工厂函数

在函数中返回对象的函数称为工厂函数,工厂函数有以下优点

  • 减少重复创建相同类型对象的代码
  • 修改工厂函数的方法影响所有同类对象

使用字面量创建对象需要复制属性与方法结构,使用工厂函数可以简化这个过程:

function createObj(name,age){
  return {
    name,
    age,
    showName(){
      console.log(`名字为${this.name}`);
    },
    showAge(){
      console.log(`${this.name}的年龄为${this.age}`);
    }
  };
}
createObj('kk',20).showName();//名字为kk
createObj('ddwb',22).showAge();//ddwb的年龄为22

构造函数

和工厂函数相似构造函数也用于创建对象,它的上下文为新的对象实例。

  • 构造函数名每个单词首字母大写即Pascal 命名规范
  • this指当前创建的对象
  • 不需要返回this系统会自动完成
  • 需要使用new关键词生成对象
function User(name){
  this.name = name;
  this.show = () => {
    console.log(name);
  }
}

let user = new User('kevin');
user.show();//kevin

在非严格模式下,this的指向为调用构造函数中方法的对象,我们在全局调用func,那么this指向的就是window对象。

<body>

</body>
<script>
  function User(title){
    this.title = title;
    this.show = function() {
      console.log(this.title);
      console.log(this);
    }
    //不需要返回,系统会自动返回
    // return this;
    //如果设置返回值,那么返回值就是设置的那个
    // retrun {}; 这时候返回值就是个空对象
  }

  let user = new User('kevin');
  user.show();
  //kevin
  //传进User构造函数返回的对象 
  let func = user.show;
  func();
  //undefined
  //window对象
</script>

严格模式

在严格模式下方法中的this值为 undefined,这是为了防止无意的修改 window 对象

<body>

</body>
<script>
  'use strict';
  function User(){
    this.show = function() {
      console.log(this);
    }
    //不需要返回,系统会自动返回
    // return this;
    //如果设置返回值,那么返回值就是设置的那个
    // retrun {}; 这时候返回值就是个空对象
  }

  let user = new User();
  user.show();
  //传进User构造函数返回的对象 User {show: ƒ}
  let func = user.show;
  func();
  //undefined
</script>

内置构造

JS 中大部分数据类型都是通过构造函数创建的。

const num = new Number(88);
console.log(num.valueOf());//88

const str = new String('kevin');
console.log(str.valueOf());//kevin

const boo = new Boolean(true);
console.log(boo.valueOf());//true

const date = new Date();
console.log(date.valueOf() * 1);//1657804424179

const regexp = new RegExp('\\d+');
console.log(regexp.test(99));//true

const obj = new Object();
obj.title = 'js';
console.log(obj);//{ title: 'js' }

字面量创建的对象,内部也是调用了Object构造函数

const obj = {};
console.log(obj.constructor);////ƒ Object() { [native code] }

对象函数

在JS中函数也是一个对象

function func(){};
console.log(func.toString());//function func(){}
console.log(func.length);//0

函数是由系统内置的 Function 构造函数创建的

function func() {}
console.log(func.constructor);//[Function: Function]

下面是使用内置构造函数创建的函数

const User = new Function(`name`,`
    this.name = name;
    this.show = ()=>{
      return this.name;
    };
  `
);

const kevin = new User('kevin');
console.log(kevin.show());//kevin

抽象特性

将复杂功能隐藏在内部,只开放给外部少量方法,更改对象内部的复杂逻辑不会对外部调用造成影响即抽象。

问题分析

下例将对象属性封装到构造函数内部

function User(name,age){
  this.name = name;
  this.age = age;
  this.show = ()=>{
    let judge = this.age > 30 ? `中年` : `青年`;
    console.log(`现在${this.name}是${judge}`);
  }
}
let kevin = new User('kevin',20);
kevin.show();//现在kevin是青年
kevin.name = 'tom';
kevin.show();//现在tom是青年

抽象封装

上例中的方法和属性仍然可以在外部访问到,比如 info方法只是在内部使用,不需要被外部访问到这会破坏程序的内部逻辑。

下面使用闭包特性将对象进行抽象处理

function User(name,age){
  let data = {name,age};
  this.show = ()=>{
    let judge = data.age > 30 ? `中年` : `青年`;
    console.log(`现在${data.name}是${judge}`);
  }
}
let kevin = new User('kevin',20);
kevin.show();//现在kevin是青年
kevin.name = 'tom';
kevin.show();//现在kevin是青年
相关文章
|
JavaScript 前端开发
JavaScript Date(日期) 对象
JavaScript Date(日期) 对象
327 2
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
Web App开发 JavaScript 前端开发
如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
|
编解码 JavaScript 前端开发
【Java进阶】详解JavaScript的BOM(浏览器对象模型)
总的来说,BOM提供了一种方式来与浏览器进行交互。通过BOM,你可以操作窗口、获取URL、操作历史、访问HTML文档、获取浏览器信息和屏幕信息等。虽然BOM并没有正式的标准,但大多数现代浏览器都实现了相似的功能,因此,你可以放心地在你的JavaScript代码中使用BOM。
514 23
|
JSON 前端开发 JavaScript
JavaScript中对象的数据拷贝
本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
408 1
JavaScript中对象的数据拷贝
|
JavaScript 前端开发 图形学
JavaScript 中 Math 对象常用方法
【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
|
存储 JavaScript 前端开发
JavaScript 对象的概念
JavaScript 对象的概念
283 4
|
存储 JavaScript 前端开发
js中函数、方法、对象的区别
js中函数、方法、对象的区别
551 2
|
JavaScript 前端开发 Unix
Node.js 全局对象
10月更文挑战第5天
296 2
|
存储 JavaScript 前端开发
js中的对象
js中的对象
204 3