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借走
固定套路:
1、Math.max/min.apply(Math,arr);
2、Object.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,()和{}之间添加=>,形参如果只有一个,则省略掉(),函数体如果只有一句话,省略{},
函数体只有一句话并且是return,return和{}都省略
特殊:千万不要将事件也简化为箭头函数 - 暂时
四、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 new类 extends 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'`。
例子2:
var 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);
上面例子二,①②③处的输出结果如下:

很明显,普通函数`outer`内部的箭头函数`fun`中的`arguments`对象,其实是沿作用域链向上访问的外层`outer`函数的`arguments`对象。
**可以在箭头函数中使用rest参数代替arguments对象,来访问箭头函数的参数列表!!**
7、箭头函数没有原型prototype
let sayHi = () => {
console.log('Hello World !')
};
console.log(sayHi.prototype); // undefined