Go to comments

JavaScript 对象的枚举

一、查看属性

obj.prop

obj["prop"]


怎么查看一个属性?

对象什么什么,增删改查都是这样


对象访问它的属性就是  对象 . 属性名 

var obj = {
  name: "abc"
}

console.log(obj.name); // 查看name属性


现在看一个对象(deng)叫老邓

1. 老邓有很多小媳妇

2. 有个 sayWife() 方法呼喊他的小媳妇,参数num传序号几,就会把第几个小媳妇返回来


正常来说用switch判断

var deng = {
  wife1: {name: "小刘"},
  wife2: {name: "小张"},
  wife3: {name: "小萌"},
  wife4: {name: "小王"},
  
  sayWife: function (num){
    switch(num){
      case 1:
        return this.wife1;
      case 2:
        return this.wife2;
      case 3:
        return this.wife3;
      case 4:
        return this.wife4;
    }
  }
}

console.log(deng.sayWife(1)); // {name: "小刘"}
console.log(deng.sayWife(2)); // {name: "小张"}
console.log(deng.sayWife(3)); // {name: "小萌"}
console.log(deng.sayWife(4)); // {name: "小王"}


不用switch判断,想实现  this.wifenum  让变量拼上属性名

var deng = {
  wife1: {name: "小刘"},
  wife2: {name: "小张"},
  wife3: {name: "小萌"},
  wife4: {name: "小王"},
  
  sayWife: function(num){
    return this.wifenum; // 变量拼属性名
  }
}

console.log(deng.sayWife(1)); // undefined
console.log(deng.sayWife(2)); // undefined
console.log(deng.sayWife(3)); // undefined
console.log(deng.sayWife(4)); // undefined


 this.wifenum  这么写就变成属性"wifenum",

查找这个"wifenum"属性肯定不行,还有一种访问属性的方法,中括号的形式 [ ]


除了  obj.name  访问形式,还可以用  obj['name'] ,方括号中间填上"字符串形式"的属性名也可以访问属性

var obj = {
  name: "abc"
}


// 下面这两种基本上是完全等同的
onsole.log(obj.name); // abc
console.log(obj['name']); // abc


有个内部原理:

obj.name --> obj['name'] 

每当  obj.name  的时候,系统内部隐式的转换成  obj['name'] 


换句话说直接写  obj['name']  在运行上还快呢,因为不需要转换了,

所以在增、删、改、查、赋值的时候都可以这么  obj['name']  来写,这是另一种表示属性的方法,只不过我们平时写点更加快捷一些


这两种(obj.name --> obj['name'])看着本质上一样,但在用法上可不一样,

1. 方括号里面必须是字符串 obj['name'],这样写不行 obj[name],直接写name相当于把变量name放进去了

2. 他俩的用法上 obj['name'] 更灵活,方括号里面是字符串,是字符串就可以拼接了


回归到刚才老邓的方法里面, this['wife' + num]  字符串加什么都等于字符串,这样就拼接了

var deng = {
  wife1: {name: "小刘"},
  wife2: {name: "小张"},
  wife3: {name: "小萌"},
  wife4: {name: "小王"},
  
  sayWife: function (num){
    return this['wife' + num]; // 字符串加什么都等于字符串,这样就拼接了
  }
}


console.log(deng.sayWife(1)); // {name: "小刘"}
console.log(deng.sayWife(2)); // {name: "小张"}
console.log(deng.sayWife(3)); // {name: "小萌"}
console.log(deng.sayWife(4)); // {name: "小王"}


想要实现属性名的拼接,只能通过方括号的形式,点是不行的,

后期会遇到很多这样属性拼接的问题,学到事件的时候 on 会加很多的东西,

比如

 on + click 

 on + mousedown 

属性拼接学到事件的时候,必须是 on 加一个什么东西去拼接


二、对象的枚举

1. for in

2. hasOwnProperty()

3. in

4. instanceof


枚举是什么意思?

枚举的英文单词是 enumeration,是遍历枚举的意思


什么是遍历呢?

给一组数据,想知道每一个数据是什么样子的,这个过程就叫做遍历的过程。


比如,

现在十个同学,想知道这十个同学每个人叫什么名字,或这十个同学每一个同学的个人信息,通过一个 for 循环挨个打印出来,挨个知道的这个过程就叫遍历的过程。


举个例子,

数组里面有好些个数据,现在想知道每一位的数据都是什么,这就是一个遍历的过程,遍历也相当于枚举(enumeration

var arr = [1,2,3,4,5,6,7,8,9];

for(var i = 0; arr.length > i; i++){
  console.log(arr[i]);
}


数组可以遍历,现在想遍历一个对象,想知道这个对象的每一个属性值是什么,

这个对象可能是后端传来的,不知道里面有什么东西,也没站到一个编辑器的角度去看,传的数据看不到,不知道循环几圈,没有 length 属性怎么办?


1、for in

想遍历一个对象必须要借助一个新的知识叫  for in  循环,就是一个简化版的for循环,能实现这个对象的遍历

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
}

for (var prop in obj) {
  console.log(prop);
}

// name
// age
// sex
// height
// weight


然后再看一下 prop 是什么类型的

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
}


for (var prop in obj) {
  console.log(prop + ': ' + typeof(prop)); // 打印"prop"的类型
}

prop都是string字符串类型的, for in  这个循环把对象里面的每一个属性名都提取出来了,而且有多少属性名就循环多少圈

name: string

age: string

sex: string

height: string

weight: string


这个循环叫  fon in  循环,它的目的只有一个,就是遍历对象用的,单独给对象设立一个循环用来便利的

 fon in  循环是通过对象属性的个数来控制循环圈数的,对象有多少个属性就循环多少圈,而且在循环每一圈的时候,会把对象的属性名放到 prop 里面(变量prop可以换名字比如换成key)


有了属性名、有了对象,我们就可以知道每一个圈,每个属性对应的属性值

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
}

for(var prop in obj){
  console.log(obj.prop); // prop代表属性名,是不是这样obj.prop就可与知道对应的属性值?
}


打印结果全是undefined,5个undefined为什么?

"prop"是字符串,是字符串为什么不报错,还能打印出undefined呢?


如果给对象obj再加一个属性  prop:123 (属性名是prop),

1. 打印结果是6个123,

2. 因为把"prop"当做属性名了

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
  prop: 123 // 这里添加一个属性prop: 123
}

for(var prop in obj){
  console.log(obj.prop); // 打印出6个123
}


为什么把"prop"当做属性名了?

1. 系统底层会有个原理,比如 obj.name 会变成 obj['name'] ,中括号里是字符串的'name'

2. 同样把规律应用到这,这么写 obj.prop 这个prop还能是变量吗?它会变成 obj['prop'] ,

    系统会理解这是在访问 prop属性,不会把prop当做变量

3. 每次循环 obj.prop 会转换成这样 obj['prop'],变成一个定量了("prop"字符串),

    每次是定量,每次就会找有没有 prop这个属性,结果就不是理想的那个样子了

4. 这样写  obj[prop] ,prop每次是变量,每次代表不同的字符串,把不同字符串放进去,相当于访问不同的属性了,

    在其他地方写没问题,但在 for in 循环枚举里写必须是方括号

5. 注意千万别 obj['prop']  这样写加引号,这样和写点没有区别了,

    方括号里的prop不用加引号  obj[prop] ,因为 prop 自身就是一个变量,变量的值都是字符串,是符合规定的


 obj[prop]  这么写会把对象里面所有的属性值遍历出来

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
  prop: 123
}

for (var prop in obj) {
  console.log(obj[prop]);
}

循环出所有属性的值

Li

37

female

167

62

123


以后写属性尽量养成一个习惯,所有对象点属性的形式,尽量写成方括号的形式


现在给对象obj手动加一个__proto__属性,手动把原来的原型改成别人,再循环遍历obj的属性,会不会把原型上的东西拿下来?

var obj = {
  name: "Li",
  age: 37,
  sex: "female",
  height: 167,
  weight: 62,
  __proto__: { // 给obj手动加一个属性__proto__
    lastName: "Fang",
  }
}

for(var prop in obj){
  console.log(obj[prop]);
}

会把原型上的东西遍历出来

Li

37

female

167

62

Fang  这是原型上的东西


如果在遍历的时候,不想把对象原型上的属性拿出来怎么办呢?

有没有一种方法判断这个属性是对象的,还是原型上的呢?有,有这个方法叫 hasOwnProperty()


2. hasOwnProperty()

任何一个对象,只要它叫对象就有 hasOwnProperty()

1. hasOwnProperty() 是个方法里面需要传参数,把对象属性名的字符串形式传进去,也就是把prop传进去

2. 这个方法是验证prop是不是对象自己的属性,然后会返回布尔值

3. 判断布尔值的结果,如果是true说明是自己属性或方法,否则说明是原型连上的


是自己的属性或方法就打印,原型链上的、原型上的就不打印了

var obj = {
  name: "Lux",
  age: 37,
  sex: "female",
  height: 172,
  weight: 62,
  __proto__: {
    lastName: "Fang"
  }
}

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(obj[prop]);
  }
}

这样加上一层判断,fang就不复存在了,因为不是对象自身的方法

Lux

37

female

172

62


 obj.hasOwnProperty(prop)  就是判断属性prop是不是对象obj自己的的,如果是对象obj自己的就打印true,如果不是就返回false,通过这样的方法来排除对象原型上的东西


注意一点:

 for in  循环理论上是可以返回原型上的东西,以及原型链上的东西,但是一旦这个原型链延展到  Object.prototype  上,不会打印终端上的,

也就是说我们手动加的__proto__,它身上还有一个__proto__,指向的是  Object.prototype ,一但延展到原型链的最顶端就会放弃,不会遍历最顶端系统自带的东西


 for in  会把自己设的原型给打印出来,但系统带的它不会打印出来,往原型链终端  Object.prototype  上加一个属性(好问题试一下)

Object.prototype.abc = "123"; // 原型链终端上加abc = "123"

var obj = {
  name: "Lux",
  age: 37,
  sex: "female",
  height: 172,
  weight: 62,
  __proto__: {
    lastName: "Fang"
  }

}

for (var prop in obj) {
  if (!obj.hasOwnProperty(prop)) { // 判断加!,意思是只打印对象原型上的属性
    console.log(obj[prop]);
  }
}

遍历结果

Fang

123


也就是说,

但凡是自己手动设置的属性,不论放到那都能打印出来,

但凡是系统自设的原型,无论在哪都一定不会出来,

所以 hasOwnProperty() 方法是用来过滤的,看看这个属性到底是不是对象自己的,当然它的应用不只在这,还有其他地方也能应用的话也能应用


但是遍历属性、遍历方法的时候,一般它们两都是成套出现的,一个 fon in 循环里面加一个 hasOwnproperty() 为了过滤一下,

一般遍历属性的时候都不希望拿原型属性,这还是遍历访问,如果修改更不需要拿原型上属性了


3. in

第二的操作符叫  in ,它和 hasOwnPorperty() 差不多,也看这个属性到底是不是这个对象的


现在看这个 height 属性到底是不是obj对象的?

会返回一个结果true或false,这么写  height in obj  行吗,会不会报错?

属性是字符串,这样直接写height不加引号是变量,所以一定把这个属性变成字符串形式'height'

var obj = {
  name: "Lux",
  age: 37,
  sex: "female",
  height: 172,
  weight: 62,
  __proto__: {
    lastName: "Fang"
  }
}

console.log(height in obj); // Uncaught ReferenceError: height is not defined


字符串形式的属性名"height",存不存在对象里?存在返回true,不存在返回false

var obj = {
  name: "Lux",
  age: 37,
  sex: "female",
  height: 172,
  weight: 62,
  __proto__: {
    lastName: "Fang"
  }
}

console.log('height' in obj); // true


这个 in 和 hasOwnproperty() 差不多,那为什么不用 in 用 hasOwnPorperty(),说明它俩有区别,

in 不分青红皂白的,你的也算你的,你父亲的也算你的。


比如 lastName 是 obj 的吗?正常来说不是,是原型上的

var obj = {
  name: "Lux",
  age: 37,
  sex: "female",
  height: 172,
  weight: 62,
  __proto__: {
    lastName: "Fang"
  }
}

console.log('lastName' in obj); // true

但是返回true,所以 in 只能判断这个对象上能不能访问到这个属性,包括原型上的,能访问返回true不能访问返回false,

它不是判断这个属性属不属于这个对象,判断这个属性属不属于这个对象只能用 hasOwnProperty()


in 操作符只能判断能不能在这个对象上调用这个属性,原型上的也算


in 操作符出现的概率,使用的概率极其极其的底,基本上就是没用过,但确实有这个东西,

入职考试时会考,比如看这几个的区别是什么,

有时候中国人骨子里就愿意搞这些形式主义,明明用不到也愿意考一下,如果换成西方绝对不会考没有用啊


in 虽然不重要,但 hasOwnProperty() 和 instanceof 是非常重要的,尤其是第三个 instanceof 


4. instanceof

 instanceof  也是操作符,必须要学好它经常考,instanceof的操作用法类似于in,但是和in是完全不同的


用法  A instanceof B  官方给出的解释是,对象A是不是构造函数B构造出来的(A是对象,B是构造函数)


对象Li是Person构造出来的返回true,然而这个用法没有这么简单,官方给的解释是完完全全不够用的

function Person(){

}

var Li = new Person();

console.log(Li instanceof Person); // true


因为在看  Li  instanceof  Object ,对象 Li 是 Object 构造出来的吗?明显不是,但结果为什么返回true

function Person(){

}

var Li = new Person();

console.log(Li instanceof Object); // true


再写一个数组,数组也是对象, 空数组 instanceof Array  是正常的,数组就是数组构造出来的

console.log([] instanceof Array); // true


虽然数组字面量也是数组构造出来的返回true,但是  []  instanceof  Object  也返回true

console.log([] instanceof Object); // true


这是什么意思?

姬成老师总结的一句话,必须记住这句话,其他的怎么记都记不完全

 "A instanceof B" 是看对象A的原型链上有没有B的原型,这个instanceof的根本语法就应该这样理解。


对象Li的原型链上,有没有构造函数Person的原型?有,顶头有第一个就是

function Person(){

}

var Li = new Person();

console.log(Li instanceof Person); // true


 Li  instanceof  Object  再看对象Li的原型链上有没有Object的原型?有,原型链最顶端就是Object.prototyep

function Person(){

}

var Li = new Person();

console.log(Li instanceof Object); // true


 Li  instanceof  Array  对象Li的原型上有没有Array?没有,Li的原型上没有Array,这个必然是false

function Person(){

}

var Li = new Person();

console.log(Li instanceof Array); // false


 {} instsanceof Person  对象字面量(空对象)的原型链上,有没有构造函数Person?

1. {} 的原型直接指向 Object.prototype 

2. 和 Perosn.prototype 没关系,没关系返回false

function Person(){

}

var Li = new Person();

console.log({} instanceof Person); // false


以前上面这样  {} instanceof Person  直接写对象字面量会报错,要把 {} 赋值一个变量,现在可能是浏览器升级不报错了

obj = {}

console.log(obj instanceof Person); // false


 A instanceof B  真正判断的是A的原型链上有没有B的原型,但是本质上是看A是不是B构造出来的,但A认为在原型链上的也算它的构造函数(构造器),但是instanceof解决了一个非常强大的问题


三、区分数组、对象的三种方法

typeof数组,返回的是object

typeof对象,返回的也是object

console.log(typeof([])) // object

console.log(typeof({})) // object


现在给一个数据,已知这个数据可能是数组也可能是对象,不一定是什么,怎么区分开?


判断一下变量arr,是数组还是对象?

var arr = [] || {};


问一个问题,

数组算不算一种对象?数组算对象。


1. 数组的constructor是  Array() { [native code] } 

console.log([].constructor); // Array() { [native code] }


2. 对象的的constructor是  ƒ Object() { [native code] } 

var obj = {};

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

区分出来了,这是第一种区分方法


第二种区分方法 

 数组  instanceof  Array  返回true

console.log([] instanceof Array); // true


 对象  instanceof  Array  返回false

var obj = {};

console.log(obj instanceof Array); // false

这样也区分出来了


还有第三种区分方法就是  toString() 

各个构造函数都重新的 toString() 方法,现在只想用 Object.prototype 上的 toString(),因为只有它 Object.prototype.toString() 能区别出数组、对象的区别


想用数组调用 Object.prototype.toString() 方法怎么做?

Object.prototype.toString.call([]);


确实用  .call( [] )  能办到,知道这是怎么回事吗?这个 Object.prototype 上的 toString() 是一个方法

Object.prototype.toString = function(){
  // this 
  // 1.首先识别this
  // 2.返回响应的结果
}


obj.toString();


正常来说这个 toString() 方法是怎么用的?

1. 正常是被谁调用了obj.toString(),

    这里面涉及一个 this 的小知识点,谁调用的它这个this就是谁。

2. 而toString()是为了处理前面调用者的,如果是对象调用它,那this就是对象。

3. toString()里面一定会用this去处理前面的东西,因为toString()把前面的的东西识别出来,再把特定的字符串格式返回。

4. toString()怎么识别前面的东西?在定义toString()方法的时候,不可能知道谁调动的它,所以只能用this,处理的是这个this。

5. 首先识别this然后返回相应的结果(this是对象就是对象,是其他东西就是其他东西)。


toString方法里面,光靠猜也知道 Object.prototype.toString()方法里面肯定会用到this,能猜到肯定会用this,

现在让 Object.prototype.toString.call() 方法执行,用 .call() 来执行,用 .call() 执行相当于空执行,

不想空执行,想让里面的this变成数组,所以数组就会替换原来的this, .call( [] 就替换this指向了,

替换this了之后,toString()里面执行的时候,原来谁调用它谁是this,现在this变成数组了,toString()就会识别数组来返回它的结果。


如果放数字 Object.prototype.toString.call(123),数字就是this,就会识别数字来当做适配对象

Object.prototype.toString.call([]); // "[object Array]"

Object.prototype.toString.call(123); // "[object Number]"

Object.prototype.toString.call({}); // "[object Object]"


Object.prototype.toString.call(???) 里面放数组打印的是数组 "[object Array]",

然后放数字打印的是数字 "[object Number]",

如果放对象就和直接调用没区别了 "[object Object]",但是数组和对象有区别,返回的字符串类型不一样。


这是区别数组和对象的第三种方法

第一种 constructor

第二种 instanceof

第三种 toString()方法



Leave a comment 0 Comments.

Leave a Reply

换一张