js 学习内容(三)ES5、ES6

218 阅读11分钟

ES5

(一)、保护对象:保护对象的成员(属性和方法)

如何保护:

    1、四大特性:
	   Object.defineProperties(obj,{
		"属性名":{
			value:实际保存值,
			writable: true/false,//开关:控制着这个属性名是否可以被修改
			enumerable: true/false,//开关:控制着这个属性名是否可以被for in循环遍历到
			configurable: true/false,//开关:控制着这个属性名是否可以被删除 - 这一句是总开关,一旦设置为false,其他特性不允许在修改,一旦设置为false不可逆
		}
	   })

     2、三个级别:
		1、防扩展:禁止给对象添加新属性
			Object.preventExtensions(obj);
		2、密封:禁止给对象添加新属性和删除属性
			Object.seal(obj);
		3、冻结:禁止给对象添加和删除和修改
			Object.freeze(obj);

		其实保护对象对于我们程序员并没有多大用处:为什么
			1、用的是面向过程开发,不用保护
			2、别人基本不可能知道我们的对象名字叫什么
			3、前辈们都没有保护,也不用保护

(二)、数组的新的API:3组6个

(1)判断:判断的结果是一个布尔值

                every:每一个 - 要求每一个元素都要满足,结果才为true,只要有一个不满足就为false - 类似于&&
			语法:var bool=arr.every(function(val,i,arr){
					return 判断条件;
			      })

				//val - 当前值
				//i   - 当前下标
				//arr - 数组本身,前辈们确实提供了3个形参给我们,但我们需要哪个在用哪个

		some:有一些 - 要求每一个元素都不满足,结果才为false,只要有一个满足就为true - 类似于||
			语法:var bool=arr.some(function(val,i,arr){
					return 判断条件;
			      })

案例:

  var arr = [1, 2, 3, 4, 5],
            arr1 = [2, 4, 6, 8, 10];
        // 判断    every 数组里面是否全都包含偶数
        var bool1 = arr.every(function (val, i) {
            return val % 2 == 0
        })

        var bool2 = arr1.every(function (val, i) {
            return val % 2 == 0
        })
        console.log(bool1)//false
        console.log(bool2)//true

        // 判断    some 数组里面是否部分包含偶数
        var bool3=arr.some(function (val, i) {
            return val % 2 == 0
        })
        var bool4=arr1.some(function (val, i) {
            return val % 2 == 0
        })
        console.log(bool3)//true
        console.log(bool4)//true

(2)、遍历

               forEach:遍历数组,直接修改原数组
			语法:arr.forEach(function(val,i,arr){
					直接做操作
			      })

		map:遍历数组,不修改原数组返回一个新数组
			语法:var newArr=arr.map(function(val,i,arr){
					return 操作的结果
			      })

(3)、过滤和汇总:

                过滤:筛选出自己想要的,但是不会修改原数组
			语法:var subArr=arr.filter(function(val,i,arr){
					return 判断条件;
			      })

		汇总:把数组中的每个元素汇总到一起
			语法:var sum=arr.reduce(function(prev,val,i,arr){
					return prev+val;
			      },基础值);//基础值会和最后的结果加在一起

案例:

            var arr = [1, 2, 3, 4, 5],
            arr1 = [2, 4, 6, 8, 10];
            var sum = 0;
             var newArr = arr.filter(function (val) {//过滤掉奇数
            return val%2!=0
        })
        console.log(newArr)//[1,3,5]

        var newArr = arr.reduce(function (prev, val) { //汇总
            return prev += val
        })
        console.log(newArr)//15

        var newArr1 = arr1.reduce(function (prev, val) { //汇总
            return prev += val
        },newArr)
        console.log(newArr1)//45

以上6个API的底层都是for循环,目的 - 简化for循环 - 以后能不写for就不写for

三、Object.create()

              根据父对象创建子对象,继承已经设置好了
	语法:var 子对象=Object.create(父对象,{
			"自有属性名":{四大特性},
			...
	      });
案例:
	                var wjl={"money":100000000000000};
			
			var wsc=Object.create(wjl,{
				"dog":{"value":"阿拉斯加"},
				"phone":{"value":"iphone100"},
			});
//			wsc.dog="阿拉斯加";
//			wsc.phone="iphone100";
			console.log(wsc);

四、严格模式

           开启:在你的任何作用域的顶部加上一句话:"use strict";
	   功能:1、禁止给未声明的变量赋值 - 解决全局污染
	         2、静默失败升级为报错

五、*call/apply/bind:不是自己的方法也可以使用

- 笔试面试也很容易碰到

     (1) call/apply:
                  临时替换了函数中的this——借用
            语法:var 变量名=函数名.call(借用的对象,实参1,实参2,....)—单独的传入每一个实参
                 var 变量名=函数名.apply(借用的对象,实参1,实参2,....只能传入一个实参,而且是一个数组,apply会自动将数组打散。
            强调:call/apply由于是临时借用,所以相当于立刻调用函数,会立即执行。
            
     (2)bind:是永久的替换了函数中的this——买
             语法:var 变量名=函数名.bind
                  3件事:
                        a/创建了一个和原函数完全相同功能的新函数;
                        b/将新函数中的this永久的绑定为了指定对象,别人都接不走
                        c/将新函数的部分参数永久固定
               用法:var 新函数=原函数.bind(指定对象,永久实参,...)——不是立刻执行,需要自己调用
               强调:bind绑定的新函数没有办法被call/apply借走
   
   固定套路:
             1Math.max/min.applyMath,arr);
             2Object.property.toString.call/apply(arr)//判断xxx是不是一个引用对象
             3、类数组转为普通数组   类数组输出格式为:NodeList() ,是一个静态集合可以用forEach
             
                             (1)类数组=Array.peoterty.slice.call/apply(类数组)
                             (2)类数组=Array.from(类数组);

ES6

简化ECMAScript - 语法较大的变化

一、*模板字符串

            可以在字符串中放入变量 - 不需要再做字符串拼接,在字符串中实现了一个简单的js的环境
	 语法:`我的名字叫${name}`

*******支持标签嵌套,需先引入jquery.js

 data.forEach((every, i) => {
        var a = `<a >
                    <div class="Imgdiv">
                        <img src='${every.img}' alt="">
                    </div>
                    <p>${every.name}</p>
                    <main class="price">
                        <span>¥${every.market}</span>
                        <span>销量${every.sales}笔</span>
                    </main>
                    <span class="span">${every.id}</span>
                </a>`
        $(".RIGHT_ONE").append(a);//循环数据渲染页面
    })

二、块级作用域

尽量以后【优先】使用let创建变量
	 let 变量名=值;
	 作用:1、禁止声明提前
	       2、添加了块级作用域,一个{}就是一个块
	       3、记录着当前触发事件的元素的下标,自带下标,不需要自定义下标
let声明的变量会作用于块作用域中,外部无法调用。(块级作用域,局部函数)
var声明的变量可以提升,而let声明的变量不能提升。(声明提前问题)
在同一个块中,let声明的变量也不能重复声明;

三、箭头函数

简化回调函数:
	 公式:去掉function,()和{}之间添加=>,形参如果只有一个,则省略掉(),函数体如果只有一句话,省略{},
	       函数体只有一句话并且是returnreturn和{}都省略
	 特殊:千万不要将事件也简化为箭头函数 - 暂时

四、for...of循环

        for(var v of 数组名){
		v;//value当前值
	 }

	 缺点:1、不能修改原数组,只能返回新数组
	       2、不能遍历hash数组,意味着也不能遍历对象,只能遍历索引数组

五、*****解构赋值

 顾名思义:解析结构后进行赋值 - 赋值的新方式,并且得到了增强
	如果赋值符号,左右两边的结构一样,就会悄悄的解开/脱掉结构再一一进行赋值
	语法:
	  1、像数组一样的解构赋值
		var [a,b,c]=[1,2,3];

	  2、像对象一样的解构赋值
		var {a,b=默认值,c}={c:值,a:值,b:值}; - 没有传值时,使用默认值

	  3、调用函数时,传递实参的顺序无所谓了
			function zwjs({name,age,hobby}){
				return `${name}今年${age}岁喜欢${hobby}`
			}
			console.log(zwjs({age:128,hobby:"打太极",name:"张三丰"}));

	  4、函数的返回的结果,可以有多个
			function f1(){
				var a=1;
				var b=2;
				return [a,b];

			}
			var [rs1,rs2]=f1();
			console.log(rs1);
			console.log(rs2);

	  之前和以后可能会碰到很多API都带有个解构赋值的操作:xx.API({})

使用结构赋值封装原生ajax方法

	var jQuery={};
			var $=jQuery;
			
			$.__proto__.ajax=({type="GET",url,data,dataType="HTML",success})=>{
				var xhr=new XMLHttpRequest();
				if(type=="GET"){
					xhr.open(type,url+"?"+data)
					xhr.send(null);
				}else if(type=="POST"){
					xhr.open(type,url);
					xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
					xhr.send(data)
				}
				xhr.onreadystatechange=()=>{
					if(xhr.readyState==4&&xhr.status==200){
						var data=xhr.responseText;
						if(dataType=="JSON"){
							data=JSON.parse(xhr.responseText);
						}else if(dataType=="XML"){
							data=xhr.responseXML;
						}
						success(data);
					}
				}
			}

六、Set和Map数据类型

set作用:去重数组

 *Set:非常的类似于数组,保存/读取数据
	语法:
	  1、创建:var s=new Set(arr);
	  2、别的API我们一个都不学,比数组差远了
	  3、将set转回数组:var arr=[...s]
          4、支持连式结构。
               如添加东西:s.add(1).add(2).add(2)//输出结果1,2
               
           
  
   Map:非常的类似于对象,保存/读取数据,没有任何卵用,还不如对象,也支持链式结构
	语法:
	  1、创建:var m=new Map();
	  2、不要记忆任何API,需要的时候,console.log输出看看原型有些什么方法

七、*****class关键字(它不是新的对象继承模型,它只是原型链的语法糖表现形式。)

简化面向对象(封装、继承、多态)开发 - react框架

老版面向对象:封装、继承不方便,多态还有可能出现覆盖的情况
	语法:
	class old类{
		constructor(name,age,hobby){//自有
			this.name=name;
			this.age=age;
			this.hobby=hobby;
		}//共有
		函数名(){

		}
	}

	class newextends old类{
		constructor(name,age,hobby,salary){//自有
			super(name,age,hobby);//自动调用你继承的old类的constructor方法
			this.salary=this.salary;
		}//共有
	}

	extends关键字会继承到old类的constructor和函数

老版面向对象 案例

老版面向对象:封装、继承不方便,多态还有可能出现覆盖的情况
	//飞行物类
			function flyer(name,speed){
				this.name=name;
				this.speed=speed;
			}
			flyer.prototype.fly=function(){
				return `${this.name}正在以时速${this.speed}km飞行`;
			}
			
			
			//飞机类
			function plane(name,speed,rl){
				this.name=name;
				this.speed=speed;
				this.rl=rl;
			}
			plane.prototype=flyer.prototype
			
			plane.prototype.fly=function(){
				return `${this.name}正在以时速${this.speed}km飞行,可以坐${this.rl}人`;
			}
			
			var bigbird=new flyer("大雕",100);
			console.log(bigbird);//flyer
			console.log(bigbird.fly());//大雕正在以时速100km飞行,可以坐undefined人
			
			var j21=new plane("歼21",1000,1);
			console.log(j21);
			console.log(j21.fly());//歼21正在以时速1000km飞行,可以坐1人

新版面向对象

也叫做:类中的继承和超集

class flyer{
				constructor(name,speed){//constructor自有属性该放的地方
					this.name=name;
					this.speed=speed;
				}//共有
				fly(){
					return `${this.name}正在以时速${this.speed}km飞行`;
				}
			}
			
			class plane extends flyer{
				constructor(name,speed,rl){//constructor自有属性该放的地方
					super(name,speed);//自动调用你继承的类的constructor方法
					this.rl=rl;
				}
				fly(){
					return `${this.name}正在以时速${this.speed}km飞行,可以坐${this.rl}人`;
				}
			}
			
			
			var bigbird=new flyer("大雕",100);
			console.log(bigbird);
			console.log(bigbird.fly());//大雕正在以时速100km飞行
			
			var j21=new plane("歼21",1000,1);
			console.log(j21);
			console.log(j21.fly());//歼21正在以时速1000km飞行,可以坐1人

八、***模块化开发 -

目的:1、分工合作 2、新的引入方式

	1、分支模块要公开
		export var xxx={}

	2、主模块要引入
		import {xxx} from "./js路径"

		特殊:
		  1、理论来说xxx要和公开的xxx名字保持一致
		  2、但是也可以修改别名{xxx as 名字},一旦改了别名就不可以再用xxx

	3、在HTML引入时:
		*****<script src="主模块.js" type="module"></script>

九、Promise对象

承诺了ajax - 三阶段有一些API里自带了此功能

	问题:ajax异步操作,没有办法保证只执行的顺序
	解决:Promise

			function ajax1(resolve){
				$.post("02.php","hide=1",(data)=>{
					document.write(`<h1>${data}</h1>`);
					resolve();
				})
			}
			
			function ajax2(){
				return new Promise((resolve)=>{
						$.post("02.php","hide=2",(data)=>{
							document.write(`<h1>${data}</h1>`)
							resolve();
						})
				})
			}
			
			function ajax3(){
				return new Promise((resolve)=>{
					$.post("02.php","hide=3",(data)=>{
						document.write(`<h1>${data}</h1>`);
						resolve();
					})
				})
				
			}
			
			new Promise(ajax1).then(ajax2).then(ajax3);

拓展补充部分

一、箭头函数和普通函数的区别****

*1、写法简单

2、箭头函数不会创建自己的this(重要!!深入理解!!)

箭头函数不会创建自己的this,所以它没有自己的this,它只会从自己的作用域链的上一层继承this。

箭头函数没有自己的`this`,它会捕获自己在**定义时**(注意,是定义时,不是调用时)所处的**外层执行环境的`this`**,并继承这个`this`值。所以,箭头函数中`this`的指向在它被定义的时候就已经确定了,之后永远不会改变。

例子:
var id = 'Global';
function fun1() {
    // setTimeout中使用普通函数
    setTimeout(function(){
        console.log(this.id);
    }, 2000);
}

function fun2() {
    // setTimeout中使用箭头函数
    setTimeout(() => {
        console.log(this.id);
    }, 2000)
}

fun1.call({id: 'Obj'});     // 'Global'

fun2.call({id: 'Obj'});     // 'Obj'

上面这个例子,函数`fun1`中的`setTimeout`中使用普通函数,2秒后函数执行时,这时函数其实是在全局作用域执行的,所以`this`指向`Window`对象,`this.id`就指向全局变量`id`,所以输出`'Global'`。\
但是函数`fun2`中的`setTimeout`中使用的是箭头函数,这个箭头函数的`this`在定义时就确定了,它继承了它外层`fun2`的执行环境中的`this`,而`fun2`调用时`this``call`方法改变到了对象`{id: 'Obj'}`中,所以输出`'Obj'`。

例子2var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};

obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'

上面这个例子,对象`obj`的方法`a`使用普通函数定义的,**普通函数作为对象的方法调用时,`this`指向它所属的对象**。所以,`this.id`就是`obj.id`,所以输出`'OBJ'`。\
但是方法`b`是使用箭头函数定义的,箭头函数中的`this`实际是继承的它定义时所处的全局执行环境中的`this`,所以指向`Window`对象,所以输出`'GLOBAL'`。(**这里要注意,定义对象的大括号`{}`是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中!!** )

3、箭头函数继承而来的this指向永远不变(重要!!深入理解!!)

上面的例子,就完全可以说明箭头函数继承而来的`this`指向永远不变。对象`obj`的方法`b`是使用箭头函数定义的,这个函数中的`this`就**永远指向**它定义时所处的全局执行环境中的`this`,即便这个函数是作为对象`obj`的方法调用,`this`依旧指向`Window`对象。

4、call()/.apply()/.bind()无法改变箭头函数中this的指向

`.call()`/`.apply()`/`.bind()`方法可以用来动态修改函数执行时`this`的指向,但由于箭头函数的`this`定义时就已经确定且永远不会改变。所以使用这些方法永远也改变不了箭头函数`this`的指向,虽然这么做代码不会报错。

var id = 'Global';
// 箭头函数定义在全局作用域
let fun1 = () => {
    console.log(this.id)
};

fun1();     // 'Global'
// this的指向不会改变,永远指向Window对象
fun1.call({id: 'Obj'});     // 'Global'
fun1.apply({id: 'Obj'});    // 'Global'
fun1.bind({id: 'Obj'})();   // 'Global'

5、箭头函数不能作为构造函数使用

我们先了解一下构造函数的new都做了些什么?简单来说,分为四步:

① JS内部首先会先生成一个对象;

② 再把函数中的this指向该对象;

③ 然后执行构造函数中的语句;

④ 最终返回该对象实例。

但是!!因为箭头函数没有自己的`this`,它的`this`其实是继承了外层执行环境中的`this`,且`this`指向永远不会随在哪里调用、被谁调用而改变,所以箭头函数不能作为构造函数使用,或者说构造函数不能定义成箭头函数,否则用`new`调用时会报错!

let Fun = (name, age) => {
    this.name = name;
    this.age = age;
};

// 报错
let p = new Fun('cao', 24);

6、箭头函数没有自己的arguments

箭头函数没有自己的`arguments`对象。在箭头函数中访问`arguments`实际上获得的是外层局部(函数)执行环境中的值。
// 例子一
let fun = (val) => {
    console.log(val);   // 111
    // 下面一行会报错
    // Uncaught ReferenceError: arguments is not defined
    // 因为外层全局环境没有arguments对象
    console.log(arguments); 
};
fun(111);

// 例子二
function outer(val1, val2) {
    let argOut = arguments;
    console.log(argOut);    // ①
    let fun = () => {
        let argIn = arguments;
        console.log(argIn);     // ②
        console.log(argOut === argIn);  // ③
    };
    fun();
}
outer(111, 222);

上面例子二,①②③处的输出结果如下:

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/15fc23eed67245829b32d6073deb979e~tplv-k3u1fbpfcp-zoom-1.image)

很明显,普通函数`outer`内部的箭头函数`fun`中的`arguments`对象,其实是沿作用域链向上访问的外层`outer`函数的`arguments`对象。

**可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表!!**

7、箭头函数没有原型prototype

let sayHi = () => {
    console.log('Hello World !')
};
console.log(sayHi.prototype); // undefined

8、箭头函数不能用作Generator函数,不能使用yeild关键字