再也不会忘记“In/hasOwnProperty/for…in/for…of/forEach/map()”

313 阅读5分钟

in

  • prop in object
  • 如果指定的属性在指定的对象或其原型链中,则in运算符返回true
  • 对于对象{},prop是其key;对于[],prop是其index或内置属性。
// 对象
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car);// true

// 数组
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");

console.log(0 in trees);        // true
console.log(3 in trees);        // true
console.log(5 in trees);        // false
console.log("bay" in trees);    // false (必须使用索引号,而不是数组元素的值)
console.log("length" in trees); // true (length是一个数组属性)
console.log(Symbol.iterator in trees); // true (数组可迭代,只在ES2015+上有效)
console.log("PI" in Math);      // true内置对象

// 删除的不同之处
delete car.make;
console.log(car); // { model: 'Accord', year: 1998 }
if ('make' in car === false) {
  car.make = 'Suzuki';
}
console.log(car.make); // "Suzuki"

delete trees[3];
console.log(trees); //[ 'redwood', 'bay', 'cedar', <1 empty item>, 'maple' ]
console.log(3 in trees); // false

hasOwnProperty()

  • obj.hasOwnProperty(prop)
  • 会返回一个布尔值,指示对象自身属性中是否具有指定的属性(不包括原型,继承)
const object1 = {};
object1.property1 = 42;
console.log(object1.hasOwnProperty('property1'));//  true
console.log(object1.hasOwnProperty('toString'));// false

var triangle = {a: 1, b: 2, c: 3};

function ColoredTriangle() {
  this.color = 'red';
}

ColoredTriangle.prototype = triangle;

var obj = new ColoredTriangle();

for (var prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    console.log(`111:obj.${prop} = ${obj[prop]}`); // 111:obj.color = red
  } else{
    console.log(`222:obj.${prop} = ${obj[prop]}`); //222:obj.a = 1 222:obj.b = 2 222:obj.c = 3
  }
}
for(var prop in obj){
    console.log(`333:obj.${prop} = ${obj[prop]}`); //333:obj.color = red 333:obj.a = 1 333:obj.b = 2 333:obj.c = 3
}

Object.getOwnPropertyNames()

  • Object.getOwnPropertyNames(obj)
  • 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
  • 不会获取到原型链上的属性。
  • 枚举属性的顺序与通过 for...in 循环(或 Object.keys)迭代该对象属性时一致。数组中不可枚举属性的顺序未定义。
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr)); // ["0", "1", "2", "length"]

// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

// 使用Array.forEach输出属性名和属性值
Object.getOwnPropertyNames(obj).forEach(function(val, idx, array) {
  console.log(val + " -> " + obj[val]);
});
// 输出
// 0 -> a
// 1 -> b
// 2 -> c

//不可枚举属性
var my_obj = Object.create({}, {
  getFoo: {
    value: function() { return this.foo; },
    enumerable: false
  }
});
my_obj.foo = 1;
console.log(Object.getOwnPropertyNames(my_obj).sort()); // ["foo", "getFoo"]

// 非对象参数被强制转换为对象 。
Object.getOwnPropertyNames('foo'); // ['length', '0', '1', '2']  (ES2015 code)

Object.keys()

  • Object.keys(obj)
  • 会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致
// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// getFoo is a property which isn't enumerable
var myObj = Object.create({}, {
  getFoo: {
    value: function () { return this.foo; }
  } 
});
myObj.foo = 1;
console.log(Object.keys(myObj)); // console: ['foo']

for...in

  • for (variable in object) statement
  • 在每次迭代时,variable会被赋值为不同的属性名。
  • 以任意顺序遍历一个对象的除Symbol以外的可枚举属性。
  • 所有可枚举的属性都会返回
  • 输出是无序的(由于对象中的属性时无序的)
  • 为遍历对象属性而构建的,不建议与数组一起使用
var obj = {a:1, b:2, c:3};  
for (const prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}
// Output:
// "obj.a = 1"
// "obj.b = 2"
// "obj.c = 3"

for...of

  • 在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句,迭代结果是值,而非属性/索引。
  • 对于for...of的循环,可以由break, throw continue 或return终止。在这些情况下,迭代器关闭。
let iterable = [10, 20, 30];

for (const value of iterable) {
    console.log(value);
} //10 20 30

let iterable = "boo";

for (let value of iterable) {
  console.log(value);
} // "b" "o" "o"
Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {   //hasOwnProperty()不迭代原型中的属性
    console.log(i); // logs 0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); // logs 3, 5, 7
}

forEach()

  • arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
  • callback为数组中每个元素执行的函数,该函数接收一至三个参数:currentValue数组中正在处理的当前元素。index 可选,数组中正在处理的当前元素的索引。array 可选,forEach() 方法正在操作的数组。thisArg 可选,可选参数。当执行回调函数 callback 时,用作 this 的值。
  • 按升序为数组中含有效值的每一项执行一次 callback 函数,那些已删除或者未初始化的项将被跳过
  • 如果 thisArg 参数有值,则每次 callback 函数被调用时,this 都会指向 thisArg 参数。如果省略了 thisArg 参数,或者其值为 null 或 undefined,this 则指向全局对象。
  • 与 map() 或者 reduce() 不同的是,它总是返回 undefined 值,并且不可链式调用。
const items = ['item1', 'item2', 'item3'];
const copy = [];

// before
for (let i=0; i<items.length; i++) {
  copy.push(items[i]);
}

// after
items.forEach(function(item){
  copy.push(item);
});

map()

  • arr.map(function callback(currentValue[, index[, array]]) {// Return element for new_array }[, thisArg])
  • 创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
  • 给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。 callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]

var kvArray = [{key: 1, value: 10}, 
               {key: 2, value: 20}, 
               {key: 3, value: 30}];

var reformattedArray = kvArray.map(function(obj) { 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});

// reformattedArray 数组为: [{1: 10}, {2: 20}, {3: 30}], 

// kvArray 数组未被修改: 
// [{key: 1, value: 10}, 
//  {key: 2, value: 20}, 
//  {key: 3, value: 30}]