ES5

严格模式

什么是: 比旧js运行机制要求更严格的运行机制
为什么: 为了屏蔽js中很多广受诟病的缺陷
何时: 今后所有代码都要运行在严格模式下
如何启用严格模式: 在当前代码段或js文件顶部添加:“use strict”; 使用 严格
新要求:
Ⅰ 禁止给未声明过的变量赋值!
① 旧js中: 可以给未声明的变量赋值,但是会在全局自动创建该变量!——极易造成全局污染,造成歧义
② 严格模式中: 禁止给未声明过的变量赋值,如果强行赋值会报错: xxx变量 未定义——避免了全局污染,也减少了歧义

"use strict";

var eric={
  eid:1001,
  ename:"埃里克"
}
//禁止修改eric的eid属性
Object.defineProperty(eric,"eid",{
  writable:false
});

eric.eid=1002;
console.log(eric);

Ⅱ 静默失败升级为错误:
① 静默失败: 程序执行不成功!也不报错!
② 旧js中: 有些操作,虽然执行不成功,也不报错!——极其不便于调试
③ 严格模式中: 静默失败升级为错误——便于调试

"use strict";

function Student(sname, sage){
//undefined
  this.sname=sname;
  this.sage=sage;
}
var lilei=new Student("Li Lei",11);
console.log(lilei);
//var hmm=Student("Han Meimei",12);
var hmm=new Student("Han Meimei",12)
console.log(hmm);
console.log(window.sname)
console.log(window.sage)

Ⅲ 普通函数调用和匿名函数调用中的this不再指向window,而是undefined!
① 旧js中: 普通函数调用和匿名函数调用中的this都指window——极易造成全局污染

arguments.callee()

Ⅳ 禁用了arguments.callee()
① 什么是arguments.callee: 专门在当前函数执行时,获得当前函数本身的关键词。
② 何时: 递归
③ 问题1: 紧耦合: 递归都是在函数内,写死当前函数的名字,如果函数名改变,必须同时修改函数内部写死的函数名。如果忘记修改,就会出错!
④ 解决: 用arguments.callee代替函数内部写死的函数名——自动获得当前函数的名字——松耦合: 函数名改变,函数内容无需修改!
⑤ 问题2: 有些递归有致命问题——极慢!——重复计算量太大!所以,严格模式禁用了arguments.callee,就是在暗示,尽量少用递归算法。
⑥ 解决: 今后绝大多数递归,都能用循环代替!

"use strict";
//斐波那契数列:
//1 1 2 3 5 8 13 21 34 55
//1 2 3 4 5 6 7  8  9  10
//数学:
//f1=1   f2=1   fn=f(n-1)+f(n-2)
//前两个月都是1,从第三个月开始,每月的数量等于相邻的前两个月的数量之和
//计算第n个月共有多少对儿兔子
//递归: 
// function f(n){
//   if(n<3){
//     return 1;
//   }else{
//     return arguments.callee(n-1)+arguments.callee(n-2)
//   }
// }
function f(n){
  if(n<3){
    return 1;
  }else{
    var f1=1, f2=1, fn;
    for(var i=3;i<=n;i++){
      fn=f1+f2;
      f1=f2;
      f2=fn;
    }
    return fn;
  }
}
console.log(f(100));
保护对象

什么是: 防止其他人对对象的结构或属性值做任何非法的修改
为什么: 因为js中的对象默认毫无自保能力!可随意修改属性值,可随意添加删除属性。
何时: 只要防止其他人对对象的结构或属性值做任何非法的修改时,都要对对象提供保护
分为两个层面的保护: 保护单个属性和保护整个对象的结构
保护单个属性:
ES5将对象的属性重新分类:
Ⅰ 命名属性: 凡是能用.访问到的属性都是命名属性
又分为两类:
① 数据属性: 直接保存属性值的属性
② 访问器属性: 不实际保存属性值,而是从另一个数据属性获取值——对另一个数据属性提供保护——保镖
Ⅱ内部属性: 不能用.访问到的属性,但是却客观存在在对象内——不需要保护
如何保护数据属性:
ES5规定,每个数据属性在内存中,其实都是一个缩微的小对象。每个属性的小对象中都包含一个value特性和三个开关

如何获取一个数据属性的缩微对象内容:
var obj=Object.getOwnPropertyDescriptor(对象, “属性名”)
获得自己的属性的描述信息
如何修改属性的缩微对象内的开关:
① 错误: 一定不能用.访问:eric.eid.writable=false
② 正确: 必须用专门的函数:

Object.defineProperty(
	哪个对象的,
	哪个属性,
	{
		开关: true/false,
		  ... : ... ,
	}
)

③ 问题1: 前脚关上的开关,其他人后脚就可随意打开!
④ 解决: 修改前两个开关时,通常都会同时设置configurable:false!以此来禁止在之后重新打开前两个开关!
⑤ 问题2: configurable:false,难道就不能被重新打开吗?
⑥ 答: 一旦configurable:false,则不可逆!(不能重新打开!)

"use strict"
var eric={
  eid:1001,//只读
  ename:"埃里克"
}
//尝试获得eid属性的缩微对象,查看其内容
var obj=Object.getOwnPropertyDescriptor(eric,"eid");
console.log(obj);
//设置eric的eid属性只读
Object.defineProperty(eric, "eid", {
  writable:false, //控制不能修改
  configurable:false //控制不能修改writable——双保险!
})

//以身试法: 
//尝试修改eid属性的值: 
// Object.defineProperty(eric, "eid", {
//   writable:true,
//   configurable:true //报错:不可逆!
// })
// eric.eid=1002;
console.log(eric);
Object.defineProperties

⑦ 问题3: 一个Object.defineProperty()只能保护对象中的一个属性。如果对象中多个属性都要保护,则需要些好几遍!
⑧ 解决: 其实如果对象中多个属性都需要保护,则可用:

Object.defineProperties(
			哪个对象的,
			{
				哪个属性: {
					开关: true/false
				},
				哪个属性: {
					开关: true/false
				},
			}
		)		
"use strict"
var eric={
  eid:1001,//只读
  ename:"埃里克",//禁止删除
  salary:12000//禁止被for in遍历
}
Object.defineProperties(eric,{
  eid:{
    writable:false,
    configurable:false
  },
  ename:{
    configurable:false
  },
  salary:{
    enumerable:false,
    configurable:false
  }
})

//以身试法: 
//尝试重新打开已经关闭的开关
// Object.defineProperty(eric, "eid", {
//   writable:true,
//   configurable:true //报错:不可逆!
// })
//尝试修改eid属性的值: 
//eric.eid=1002;
//尝试删除ename属性
//delete eric.ename;
//尝试遍历eric中所有属性(包括salary)
for(var key in eric){
  console.log(`${key} : ${eric[key]}`)
}
console.log(eric);

⑨ 问题4: 如果所有员工对象都需要被保护:
⑩ 解决: 将保护放在创建员工对象的构造函数里

"use strict"
//如果希望反复创建员工对象,一定会定义构造函数
function Emp(eid, ename, salary){
  //以.方式强行添加的属性,默认所有开关都是开着的!
  // this.eid=eid;
  // this.ename=ename;
  // this.salary=salary;
  Object.defineProperties(this,{
    //如果对象中没有要设置的属性,则自动添加该属性,只不过必须写完整value+三个开关!不能偷懒!
    eid:{
      value:eid,
      writable:false,
      enumerable:true,
      configurable:false
    },
    ename:{
      value:ename,
      writable:true,
      enumerable:true,
      configurable:false
    },
    salary:{
      value:salary,
      writable:true,
      enumerable:false,
      configurable:false
    }
  })
}
var eric=new Emp(1001, "埃里克", 12000)
for(var key in eric){
  console.log(`${key} : ${eric[key]}`)
}
console.log(eric);
访问器 半隐藏

如何使用访问器属性来保护数据属性:
问题: 数据属性虽然都有三个开关,但是保护太弱,不够灵活!
什么是访问器: 自己不保存属性值,而是专门提供对另一个数据属性的保护
何时: 只要数据属性自己保护不了自己时,就可请访问器属性当保镖!
如何:
① 新建一个_eage属性,半隐藏,并把原eage属性值搬家到_eage属性中。——隐姓埋名,半隐藏
② 创建一个与原eage属性同名的访问器属性,当保镖,冒名顶替原eage属性,并保护半隐藏的_eage属性。其中,保镖一请,就是2个人
i. get()函数,专门负责从受保护的_eage属性中获取属性值
ii. set(value)函数,专门负责将要赋值的新值保存到受保护的_eage属性中实际保存起来。
结果: 外人,访问eage访问器属性,以为访问的还是原eage属性。
① 当外人试图获取eage的属性值时,eage访问器属性会自动调用内部的get()函数,从受保护的_eage属性中取出属性值
② 当外人试图修改eage属性值时,eage访问器属性自动调用内部的set(value)函数,并将要赋值的新值传递给set的参数变量value。由set()函数经过验证,再决定是否将新值value保存到受保护的属性_eage中.

	var eric={
  		ename:"埃里克",
  		eage:25 //必须18~65之间
	}
	//2步:
	//1. 为eric添加一个_eage属性,且半隐藏,然后将原eage属性值保存到_eage中
	Object.defineProperties(eric,{
  		_eage:{ //隐姓埋名,半隐藏
    		value:eric.eage,//搬家
    		writable:true,
    		enumerable:false,
    		configurable:false
  		},
  		//2. 定义一个新的访问器属性eage,冒名顶替原eage属性
  		eage:{
    		//专门负责返回受保护的_eage属性值
    		get:function(){
      			return this._eage;
    		},
    		//专门负责修改受保护的_eage属性值
    		set:function(value){
      			//value会自动接到要赋值的新值
      			if(value>=18&&value<=65){
        			this._eage=value;
      			}else{
        			throw Error("年龄必须介于18~65之间")
      			}
    		},
    		//保镖顶替受保护的属性抛头露面
    		enumerable:true,
    		configurable:false
  		}
	})//外人: 
	//尝试输出eage属性值: 都会自动调用保镖的get()函数
	console.log(eric.eage);
	//尝试修改eage的属性值为正确的值: 会自动调用保镖的set(27)函数
	eric.eage=27; //正常
	console.log(eric);
	//尝试修改eage的属性值为错误的值: 会自动调用保镖的set(-2)函数
	eric.eage=-2; //报错
	console.log(eric);

问题: 为什么访问器属性eage内部的get()和set()中,可以用this访问eric对象的_eage属性?
答: 原eage访问器属性内部的{ get(), set() }两个函数,
编译后被打散,get()和set()两个函数直接隶属于eric对象,和_eage属性平级,所以可以this._eage
在这里插入图片描述

保护结构
(1). 防扩展: Object.preventExtensions(obj)
(2). 密封: Object.seal(obj);
(3). 冻结: Object.freeze(obj)

保护对象结构:
问题:

  • 属性的开关,只能防止删除现有属性,无法防止添加新属性
  • 如果每个属性都禁止删除的话,只能反复写configurable:false!
  • 什么是保护对象结构: 禁止外人为对象添加新属性,或删除现有属性
  • 包括三个级别:
防扩展 Object.preventExtensions(obj)

①禁止给对象添加新属性
② Object.preventExtensions(obj)
阻止 扩展
③结果: 禁止了给obj对象添加新属性
④原理: (了解)
i. 其实每个对象内部都有一个隐藏的属性extensible:true
ii. preventExtensions()函数,将对象的extensible:false

"use strict";
var eric={
  eid:1001,
  ename:"埃里克"
}
//阻止对eric对象添加新属性
Object.preventExtensions(eric);
//尝试添加新属性
//eric.Eid=1002;
//尝试删除现有属性
delete eric.ename;
console.log(eric);
密封 Object.seal(obj)

① 问题: preventExtensions()函数只能防止添加新属性,不能防止删除现有属性
② 解决: 用密封
③ 什么是密封: 既阻止添加新属性,又阻止删除现有属性
④ 如何:Object.seal(obj)
⑤ 结果: 既阻止向对象obj中添加新属性,又阻止从obj对象中删除现有属性
⑥ 原理: 2件事:
i. 将对象的extensible:false
ii. 还自动将对象中所有属性的configurable:false
⑦结论: 今后只要保护对象结构,都用Object.seal()即可,同时,不用在每个属性都写configurable:false

"use strict";
var eric={
  eid:1001,
  ename:"埃里克"
}
//密封: 既阻止对eric对象添加新属性,同时又阻止删除eric对象的现有属性
Object.seal(eric);
//尝试添加新属性
//eric.Eid=1002;
//尝试删除现有属性
//delete eric.ename;
console.log(eric);
冻结 Object.freeze(obj)

① 既禁止添加和删除属性,甚至禁止修改所有属性值
Object.freeze(obj)
③结果: 既禁止给obj对象添加和删除属性,设置禁止修改obj对象中所有属性值
④何时: 如果多个模块共用的对象,就不应该被其中某一个模块擅自修改。
⑤ 原理: 3件事:

  1. 将对象的extensible:false
  2. 还自动将对象中所有属性的configurable:false
  3. 自动把每个属性的writable:false
"use strict";
var dbconfig={
  ip:"127.0.0.1",
  port:3306,
  db:"xz"
}
Object.freeze(dbconfig);
//试图添加新属性:
//dbconfig.Ip="localhost";
//试图删除现有属性: 
//delete dbconfig.db;
//试图修改现有属性的值
dbconfig.ip="localhost";
总结: 保护对象

保护属性:
(1). 保护数据属性:
a. 只保护一个属性:

	Object.defineProperty(对象, "属性名", {
		开关: true/false,
		configurable:false
	})

b. 保护多个属性:

Object.defineProperties(对象,{
		属性名:{
			开关: true/false,
			configurable:false
		},
		属性名:{
			开关: true/false,
			configurable:false
		},
	})

c. 保护所有孩子的属性:

	function 构造函数(形参列表){
		Object.defineProperties(this,{
			属性名: {
				value: 形参,
				writable:true/false,
				enumerable:true/false,
				configurable: false
			},
			属性名: {
				value: 形参,
				writable:true/false,
				enumerable:true/false,
				configurable: false
			},
		})
	}

d. 其实构造函数里也可写访问器属性(了解)
但是访问器属性本质其实是一个函数,所以,最好将访问器属性放在原型对象中, 最像Java的构造函数

创建子对象继承 Object.create();
  1. 什么是: 创建子对象,继承现有的父对象
  2. 为什么: 有时,没有构造函数,也想创建子对象
  3. 何时: 只要在没有构造函数的情况下也想创建子对象,继承父对象时,就用Object.create()
  4. 如何:
var 子对象=Object.create(
		父对象,
		{ //为子对象添加自有属性: 方式类似于definePropeties()
			属性名:{
				value:属性值,
				开关: true/false,	
				... : ...
			},
			属性名:{
				value:属性值,
				开关: true/false,	
				... : ...
			}
		}
	)
  1. 结果:
    (1). 创建一个新的空的子对象
    (2). 让新的子对象,继承指定的父对象
    (3). 为子对象添加自有属性
var father={
  bal:10000000000,
  car:"infiniti"
}
var hmm=Object.create(father,{
//defineProperties()
  bao:{
    value:"LV",
    writable:true,
    enumerable:true,
    configurable:false
  },
  phone:{
    value:"iPhone11",
    writable:true,
    enumerable:true,
    configurable:false
  }
});
//2件事:
//1. 创建空对象hmm
//2. 设置空对象hmm的__proto__继承father
//3. 为新对象hmm,添加自有属性: bao和phone
console.log(hmm);
console.log(hmm.bal, hmm.car);
替换函数中的this
  1. 什么是: 将函数中不想要的this,指向想要的对象
  2. 何时: 只要一个函数中的this不是想要的,都可修改!
call

如何: 2种情况
在本次调用函数时,临时替换一次this为指定的对象
默认: 要调用的函数.call(替换this的对象, 其他实参列表)
call做了3件事:
① Call是调用函数的意思,所以会调用函数执行一次
② Call可自动将函数内的this临时替换为call的第一个参数
③ 将实参值列表传给函数的形参变量!

//假设有一个公共的计算器,可以计算总工资
//            底薪   奖金1   奖金2
function calc(base, bonus1, bonus2){
  console.log(`${this.ename}的总工资是:${base+bonus1+bonus2}`)
}

var lilei={ename:"Li Lei"}
var hmm={ename:"Han Meimei"}

//李磊想用计算器计算自己的薪资: 
//错误: lilei.calc(10000,2000,3000);
//因为: calc不在李磊对象的原型链上!
//正确: 因为calc是全局函数,所以调用时前边什么都不能加
//错误: calc(10000,2000,3000);
//因为: calc调用时,前边什么都没有,所以this->window,而我们希望this->lilei
//正确: 
calc.call(lilei,10000,2000,3000)
//   调用
//3件事: 
//1. 调用calc函数执行
//2. *替换calc中的this为lilei*
//3. 将实参值列表传给函数的形参变量!
//但是,是临时替换一次!
//calc(10000,2000,3000);
//韩梅梅也想使用计算器
calc.call(hmm,3000,4000,5000)
apply

问题: 如果要传入函数的实参值列表是放在一个数组中整体传入的,而函数的形参列表却要求分别接住每个对应实参值,就出现了不一致!
解决: 用apply()代替call()
要调用的函数.apply(替换this的对象, 保存实参值的数组) f. apply()做了3件事:
① Apply也是使用函数的意思,所以也会调用函数执行一次
② Apply也可自动将函数内的this临时替换为apply的第一个参数
③ 但是, apply可先将数组打散为单个值,再顺序传入函数中,交给对应的形参变量!

bind

基于原函数创建一个一模一样的新函数副本,并永久替换新函数中的this为指定对象
var 新函数=原函数.bind(替换this的对象, 部分实现值)
bind:3件事:
① 创建一个和原函数一模一样的函数副本
永久替换函数副本中的this为指定的对象
③ 同时还替换函数副本中的部分形参为固定的值!

//假设有一个公共的计算器,可以计算总工资
//            底薪   奖金1   奖金2
function calc(base, bonus1, bonus2){
  console.log(`${this.ename}的总工资是:${base+bonus1+bonus2}`)
}

var lilei={ename:"Li Lei"}
var hmm={ename:"Han Meimei"}

//李磊想买一个自己专属一模一样的计算器!
//创建原calc函数的一个副本,并永久修改副本中的this为lilei对象
var lcalc=calc.bind(lilei,10000);
//                    ↓     ↓
//  lcalc        ( this, base)
//  lcalc        (            bonus1,bonus2)
//从此:
lcalc(1000,2000);
lcalc(2000,3000);
//hmm想抢李磊专属的计算器函数
lcalc.call(hmm,4000,5000)

结果:
① 从此使用函数副本,不用再反复call(),反复传替换this的对象
② 被bind绑定的this,即使再用call也无法修改

总结: call apply bind

  1. 如果只临时替换一次this,call或apply
    (1). 如果需要传入多个参数值,而给的参数值也是多个,没有出现不一致,则首选call()
    (2). 如果需要传入多个参数值,但是给的参数值却是放在一个数组中给的,出现不一致,则用apply可打散数组后再传参
  2. 如果要永久替换一个函数中的this,反复使用时,采用bind
    记住: 今后如果想更换回调函数中的this,一定用bind!
    因为回调函数别调用几次,咱们不知道!
ES5 新增数组函数
Every
  • 判断:
    (1). 判断数组中是否每个元素都符合要求
 var bool=arr.every(//要求提供一个回调函数参数
	//回调函数要求
	function(elem, i, arr){ //1. 三个形参
		return 判断条件 //2. 
  	}
  )
  强调: 形参变量,elem,i,arr可随意改名
原理:
① Every中自带for循环,自动遍历arr中每个元素
② 每遍历到一个元素,就自动调用一次回调函数
③ 每次调用回调函数时,都自动传入三个实参值给回调函数: 
  • elem: 获得当前正在遍历的一个元素值
  • i : 获得当前正在遍历的位置
  • arr : 获得当前正在遍历的完整数组对象的引用
    ④ 回调函数内,根据传入的实参值,验证当前正在遍历的到一个元素是否符合要求,并返回判断结果
    ⑤ Every只要调用回调函数时碰上一个返回false,就立刻停止遍历,返回最终结论false,表示当前数组不是所有元素都符合要求
    ⑥ 如果Every可以走到最后元素,所有元素经过判断都返回true,才返回最终结论true,表示当前数组中所有元素都符合要求
    示例: 判断哪个数组全由偶数组成
var arr1=[1,2,3,4,5];
var arr2=[2,4,6,4,2];
//判断哪个数组*都是*由偶数组成
var result1=arr1.every(
  function(elem){
    console.log(`arr1.every()自动调用一次回调函数,elem=${elem},返回${elem%2==0}`)
    //判断当前元素是不是偶数!
    return elem%2==0
  }
)
var result2=arr2.every(
  function(elem){
    console.log(`arr2.every()自动调用一次回调函数,elem=${elem},返回${elem%2==0}`)
    //判断当前元素是不是偶数!
    return elem%2==0
  }
)
console.log(result1, result2)
//           false    true

d. 示例: 判断哪个数组是升序

var arr1=[1,2,3,4,5];
var arr2=[2,4,6,4,2];
//哪个数组是升序排列?
//*每个*元素都<=它的后一个元素
var result1=arr1.every(
  function(elem,i,arr){
    //如果i没到最后一个位置
      //*每个*元素都<=它的后一个元素
    //否则如果i已经是最后一个位置
      //直接返回true
    return i<arr.length-1?elem<=arr[i+1]:true
  }
)
var result2=arr2.every(
  function(elem,i,arr){
    //如果i没到最后一个位置
      //*每个*元素都<=它的后一个元素
    //否则如果i已经是最后一个位置
      //直接返回true
    return i<arr.length-1?elem<=arr[i+1]:true
  }
)
console.log(result1, result2)
//           true     false
some

判断数组中是否包含一些符合条件的元素

var bool=arr.some(function(elem,i,arr){
	return 判断条件
  })

原理:
① some中自带for循环,自动遍历arr中每个元素
② 每遍历到一个元素,就自动调用一次回调函数
③ 每次调用回调函数时,都自动传入三个实参值给回调函数:同every
④ 回调函数内,根据传入的实参值,验证当前正在遍历的到一个元素是否符合要求,并返回判断结果
⑤ some只要调用回调函数时碰上一个返回true,就立刻停止遍历,返回最终结论true,表示当前数组包含至少一个符合要求的元素
⑥ 如果some走到最后元素,所有元素经过判断都返回false,才返回最终结论false,表示当前数组中不包含任何一个符合要求的元素。
示例: 判断哪个数组中包含奇数:

var arr1=[2,4,6,8,10];
var arr2=[1,2,3,4,5];
//判断按个函数中*包含*奇数
var result1=arr1.some(function(elem){
  console.log(`arr1.some(elem=${elem})自动调用了一次回调函数, 判断结果: ${elem%2==1}`)
  return elem%2==1
})
var result2=arr2.some(function(elem){
  console.log(`arr2.some(elem=${elem})自动调用了一次回调函数, 判断结果: ${elem%2==1}`)
  return elem%2==1
})
console.log(result1, result2)
//           false     true
forEach

仅遍历原数组中每个元素

arr.forEach(function(elem,i,arr){
		对每个elem执行相同的操作
	  })
  强调: 回调函数中不用返回值

原理:
① forEach中自带遍历,自动遍历数组中每个元素
② 每遍历一个元素,就自动调用回调函数,自动传入当前元素值elem、当前位置 i、当前数组对象arr
③ 在回调函数内部,对每个元素执行相同的操作,无需返回值

  • 何时: 只要遍历索引数组中每个元素,都可用forEach代替普通for循环,因为可以进一步用es6简写为箭头函数!
  • 强调: 类数组对象和字符串虽然也是数字下标,但是不能使用forEach,因为他们不是数组家孩子!
  • 示例:遍历数组中每个元素:
var names=["qq","ww","ee"];
//普通for循环遍历
// for(var i=0;i<names.length;i++){
//   alert(`${names[i]} - 到!`)
// }
//forEach遍历
names.forEach(function(elem){
  alert(`${elem}-到!`)
})
//更简化
//names.forEach(elem=>alert(`${elem}-到!`))

示例: 将原数组中每个元素值*2

var arr=[1,2,3,4,5];
//想对原数组中每个元素值*2
//for循环
// for(var i=0;i<arr.length;i++){
//   arr[i]*=2;
// }
//forEach
arr.forEach(
  //      [  1  ,  2  ,  3  ,  4  ,  5]
  //         ↓按值传递: 原始类型传副本
  function(elem,i,arr){
  //         1 副本
    //错: elem*=2; 
    //在函数内修改副本不影响原数组中的元素
    //正确: 
    arr[i]*=2
    //用数组地址访问数组中的元素等效于直接修改原数组内容
  }
)
//arr.forEach((elem,i,arr)=>arr[i]*=2)
console.log(arr);
var arr2=[
  /*0:0x9091----------->*/{x:1},
  /*1:0x3456->*/{x:2},
  /*2:0x7812----->*/{x:3},
  /*3:0x1234------------------>*/{x:4},
  /*4:0x9012---------->*/{x:5}
]
//想对原数组中每个对象的x属性值*2
arr2.forEach(
  //     [0x9091,0x3456,0x7812,0x1234,...]
  //        ↓按值传递: 引用类型传地址
  function(elem){
    //    0x9091
    elem.x*=2;
  //0x9091.x*=2 //直接通过地址修改原对象内容
  }
)
//arr2.forEach(elem=>elem.x*=2)
console.log(arr2);
Map

遍历原数组中每个元素,执行相同操作后,放入新数组返回
何时 : 只要希望保护原数组,生成一个改造后的新数组,都用map

var 新数组=arr.map(function(elem,i,arr){
		return 当前元素改造后的新元素值
	  })

原理:
① Map先创建一个空数组等着
② 然后才自带遍历,自动遍历原数组中每个元素
③ 每遍历一个元素就自动执行一次回调函数,并自动传入三个参数: 当前元素值elem, 当前位置i, 当前数组对象arr
④ 在回调函数中,对获得的当前元素值,加工后,返回出来给map函数
⑤ Map获得回调函数返回的新元素值后,自动放入新数组中相同位置.
⑥ 遍历结束,map会将新数组返回出来!

示例: 取出原数组中值,*2后,放入新数组中,原数组保持不变

var arr=[1,2,3,4,5];
//希望原数组保持不变,又能对原数组中元素都*2后,放入新数组返回!
var newArr=arr.map(
  //var newArr=[];
  function(elem){
    return elem*2;
  }
  //return newArr;
)  
console.log(newArr);
console.log(arr);

示例: 根据数组内容,动态加载ul中的li内容

var names=["qq","ww","ee"];
//根据数组内容,动态加载ul中的li内容
//for循环:jQuery中
// //先定义一个空字符串:
// var html="";
// //然后遍历数组中每个元素
// for(var i=0;i<names.length;i++){
//   //每遍历一个元素,就向空字符串中拼接一个<li>下标+1 - 元素值</li>
//   html+=`<li>${i+1} - ${names[i]}</li>`
// }
// //将拼接后的html内容,填充到ulNames的内容中
// ulNames.innerHTML=html;

//map方式: react框架中!
//将原数组中每个元素都转化为"<li>下标+1 - 元素值</li>"字符串,放入新数组中返回
var newArr=names.map(function(elem,i){
  return `<li>${i+1} - ${elem}</li>`
})
console.log(newArr);
//将新数组中每个<li>字符串无缝拼接为一个大的HTML片段
var html=newArr.join("");
console.log(html);
//将HTML片段填充到ulNames的内容中
ulNames.innerHTML=html

//map方式可简化为: 
//ulNames.innerHTML=names.map(
//  (elem,i)=>`<li>${i+1} - ${elem}</li>`
// ).join("")
过滤和汇总

过滤: 复制出数组中符合条件的元素,放入新数组中返回,原数组保持不变

 var 新数组=arr.filter(function(elem,i,arr){
		return 判断条件
	  })
 原理:
	①  Filter先创建一个空数组等待
	②  Filter也自带遍历,自动遍历数组中每个元素
	③ 每遍历一个元素,就自动调用一次回调函数,并自动传入三个参数: 当前元素值elem, 当前位置i, 当前数组对象arr
	④ 在回调函数内,通过一个条件判断当前元素是否符合要求,并返回判断结果给filter函数
	⑤  Filter函数每收到一个判断为true的元素,就追加到新数组中
	⑥  最后,返回新数组

示例: //想只取出arr中的偶数

var arr=[1,2,3,4,5];
//想只取出arr中的偶数
var newArr=arr.filter(
  //var newArr=[]
  //for(var i=0;i<arr.lenght;i++){
    function(elem){
      //只有偶数才加入新数组中
      return elem%2==0
    }
    //newArr.push(判断结果为true的元素)
  //}
  //return newArr;
)
console.log(newArr);
console.log(arr);

汇总: 对数组中所有元素进行统计,求出最终的统计结果

var 结果=arr.reduce(
		function(prev, elem, i, arr){
			return 新的临时的汇总结果
	  	}, 
		起始值
	  )

原理:
① Reduce中也自带遍历,自动遍历原数组中每个元素
② 每遍历一个元素,就自动调用回调函数,并传入4个参数值:

  • prev: 截止到当前元素之前的临时汇总值
  • elem: 当前元素值
  • i : 当前位置
  • arr: 当前数组对象
    在回调函数中,应该将当前元素值elem汇总到截止目前的临时汇总值prev中,形成新的汇总值,返回
    ③ Reduce函数会用返回的新的临时汇总值,代替旧的临时汇总值
    ④ 遍历结束,剩下的汇总值,就是最终的汇总结果
    示例: 想对arr1数组中的所有元素求和
var arr1=[1,3,5,7,9];
//想对arr1数组中的所有元素求和
var result=arr1.reduce(
  function(prev,elem){
    //将截止目前的临时汇总值于当前元素值相加,获得新的临时汇总值返回!
    return prev+elem
  },
  0 //简写: 如果省略起始值,则自动将第一个元素值作为起始值——不推荐省略
)
console.log(result)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dev _

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值