ES6(JS)

4 篇文章 1 订阅

目录

JS

函数的三种写法

引用类型

字符串匹配

json与jsonp

JS的数据类型

Object的一些方法

空值类型

typeof 与 instanceof 

深copy与浅copy

类数组

定义

类数组转化为数组

Function与function的区别

== 与 ===

call()、apply()、bind()

作用域 

块级作用域(私有作用域,ES5)

私有变量

闭包

什么是闭包

闭包的作用

this(多方面)

内存管理

内存分配

内存溢出

内存泄漏

垃圾回收

AJAX

方法

AJAX的工作原理 

jQuery的AJAX

 $.ajax({})

axios

JSON.stringify()与qs.stringify()

传参编码

JS的节流和防抖

 var、 let、 const(ES6)

解构

扩展运算符 

箭头函数

this作用域

箭头函数与function的区别

箭头函数获取 arguments 

Iterator遍历器

原理

for...in与for...of

字符串(ES6)

set

add()、delete()、size、 clear()、has(value)

set转化为数组,两种方法

遍历操作

WeakSet

map

基本操作

其他数据类型转化为map

遍历方法

WeakMap

symbol

异步编程

(todo)

Promise(ES6)

Generator

async

事件

事件冒泡

事件捕获

事件委托(事件代理)

事件处理

事件类型

JS的异步模式

回调函数

事件监听

Promises异步函数

怎样解决跨域问题 

eval

如何理解前端模块化

手动实现bind函数

实现JS所有对象的深度克隆


JS

JS是由ES(ECMAScript)DOM(浏览器文档对象)、BOM(浏览器对象模型)组成

函数的三种写法

定义函数的方式有两种:1.函数声明  2.函数表达式

需要注意的是:函数声明方法,在执行代码之前会先读取函数声明,所以调用函数可以写在声明之前;而函数表达式,需要先写出表达式,再调用

// 函数的三种写法
//函数声明:function关键字+函数的名字
function person(a, b){
    return a+b;
}
a = person(1, 1);
console.log("a ", a);

//函数表达式: 匿名方法
let person = function(){
    return 2;
}
console.log("a ", person());  ///a  2

//将方法作为一个对象
var obj = {
    person:function(a, b){
        return a+b;
    }
}
a = obj.person(1, 1);
console.log(a);

引用类型

对象是某个特定引用类型的实例

Object类型

大多数引用类型都是Object类型。创建object实例有两种,第一种通过new Object();第二种对象字面量法,这种适用于具有大量属性的对象。

var a = new Object();
a.name = 'lhf';

var b = {
  name: 'lhf'
}

Arrary类型

创建array实例也有两种方式,Array.isArray(object),检测一个对象是不是数组类型。

Function类型

字符串匹配

search() : 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串,并返回子串的起始位置

i  是一个修饰符 (搜索不区分大小写)

let str = "8visit run"
let n = str.search(/visi/i)

test方法检测一个字符串是否匹配某个正则表达式

// 正则表达式 检测一个字符串是否全为数字
let str = "12345"
let re = /^[0-9]+$/.test(str)
console.log(re);  // true

exec()正则匹配字符串

let pattn = new RegExp("[a-z]*")
let re = pattn.exec("ascd")
console.log(re);

json与jsonp

json: 是一种数据格式,两种数据类型描述符,大括号{}和方括号[]

jsonp:和ajax相同,都是客服端向服务端发送请求,从服务端获取数据的方式,但是ajax属于同源策略,jsonp是非同源策略(跨域);

(两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源)

当需要通讯时,本站脚本创建一个
并提供一个回调函数来接收数据

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
</head>
<body>
<div id="divCustomers"></div>
<script type="text/javascript">
    function demo1(data) {
        console.log(data);
    }
</script>	
<script type="text/javascript" src="http://118.178.197.33:8001/recruit_web/get_position_list/"></script>
</body>
</html>
return HttpResponse("demo1({})".format(json.dumps(result)))

JS的数据类型

  • 基本类型(简单类型、原始类型):String、Number、Boolean、Null、Undefined、Symbol(es6)。数据存放在栈内存中,可直接访问和修改变量的值;
  • 引用类型(复杂类型):Object、Array、Function、Date等。存放在堆内存中,栈内存只保存一个指针,访问时需要由指针找到数据;

空值类型

let a ;                //underfine
let b = null;
console.log(a == b);   // true
console.log(a === b);  // false

typeof undefined       // undefined
typeof null            // object

console.log(Number(a)); //NaN
console.log(Number(b)); //0

//判断方法
function judgeNull(a){
    if(!a && (typeof a) == 'object'){
        return true;
    }
    return false;

}

function judgeUnderfine(a){
   if(!a && (typeof a) == 'undefined'){
       return true;
   }
   return false;
}

null

  • null可以被Number()函数转化为0
  • 表示一个值被定义了,定义为“空值”

undefined(表示缺少值

  • 变量被声明了,但并没有赋值
  • 函数没有返回值时,默认返回undefined

typeof 与 instanceof 

  • typeof返回的是该对象的类型。null的类型是object;对于对象类型能判断出function、其他的都为Object
let a;
let b = null;
console.log(typeof(a)); //undefined
console.log(typeof(b)); //object
  • A instanceof B,判断A是不是B的实例(可以判断继承吗
  • 不能判断基础类型
function Person(name, age) {
  this.age=age;
  this.name=name;
}
let person1 = new Person('lhf', 24);
console.log(person1 instanceof Person); //true

let arr = new Array();
console.log(arr instanceof Array); //true
let date = new Date();
console.log(date instanceof Date); //true

深copy与浅copy

下面只讨论引用类型

1.引用对象简单的赋值,只是复制了指针,两个指针指向的是同一个对象

一层都不能copy

let a = [1,2,3,4];
let b = a;
b.push(5);
console.log(a); //[ 1, 2, 3, 4, 5 ]
console.log(b); //[ 1, 2, 3, 4, 5 ]

2.Object.assign(target, ...sources)

此方法可以将多个源对象copy到目标对象,如果有相同的值则覆盖到源对象,结果会返回源对象

const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const result = Object.assign(target, source);
console.log(target); //{ a: 1, b: 4, c: 5 }
console.log(source); //{ b: 4, c: 5 }
console.log(result); //{ a: 1, b: 4, c: 5 }

1.深层递归遍历

2.JSON.stringify()

(无法实现对象中方法的copy)

let a = [1,2,3,4];
let b = JSON.stringify(a);
b = JSON.parse(b);
b.push(5);
console.log(a); //[ 1, 2, 3, 4 ]
console.log(b); //[ 1, 2, 3, 4, 5 ]

类数组

定义

JavaScript 类型数组将实现拆分为缓冲视图两部分。一个缓冲(ArrayBuffer对象)描述的是一个数据块,需要使用视图去访问缓冲对象的内存。

ArrayBuffer(缓冲):用来表示一个固定长度的二进制缓冲区;

DataView(视图):提供有可以操作缓冲区中任意数据的读写接口;

//创建一个16字节固定长度的缓冲
var buffer = new ArrayBuffer(16);       
// 创建一个视图,此视图将把缓冲内的数据格式化为一个32位的有符号整数数组
var int32View = new Int32Array(buffer);
// 可以像普通数组一样访问该数组中的元素
for (var i = 0; i < int32View.length; i++) {
  int32View[i] = i * 2;
}
console.log(int32View);         //Int32Array(4) [ 0, 2, 4, 6 ]
console.log(typeof int32View);  //object

//一个字节由8位组成,16个字节就是128位,格式化为32位的数据,就是4个,所以该数组有4个元素

类数组不是数组,数组可以用Array.isArray(obj)来检测

例如argument,函数的第一个参数是argument[0],写法上跟数组一样,但是不是数组,他的原型是Object

例如获取的dom节点

let ps = document.querySelectorAll('p');

类数组转化为数组

Array.prototype.slice

nomalArray = Array.prototype.slice.call(int32View);
console.log(nomalArray);                 //[ 0, 2, 4, 6 ]
console.log(Array.isArray(nomalArray));  //true

Function与function的区别

function 是 js 的标识符

Function 是 js 里面的一个 构造函数,通过new创建

== 与 ===

== 

  • 如果两个值类型相同,再进行三个等号(===)的比较
  • 如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:

    1)如果一个是null,一个是undefined,那么相等

    2)如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较

===

  • 如果类型不同,就一定不相等;
  • 如果是相同类型(数字、字符串、boolen)、值不同,则不相等;
  • 如果其中一个值为null,则不相等;
  • 如果两个值都引用同一个对象或是函数,那么相等,否则不相等;

call()、apply()、bind()

这里参考的连接:2.javascript中call()和apply()区别 - 林玖女神 - 博客园

apply() 接受两个参数,第一个指定了函数体内this的指向,第二个参数是一个集合,数组或者类数组;如果第一个参数是null,那么this指向全局

let func = function(a,b,c){
  console.log([a,b,c])
}
func.apply(null,[1,2,3])  // [1,2,3]

call(),接受的参数不固定,第一个参数指定了this的指向,后面的参数依次被传入函数

let func = function(a, b, c) {
  console.log(a, b, c);
};
func.call(null, [1, 2, 3]);  //[ 1, 2, 3 ] undefined undefined

bind(),  主要是将函数绑定到某个对象,bind(obj,...)函数体内的this会被绑定到bind的第一个参数上,f.bind(obj), 可以理解为obj.f() ,f函数内的this指向obj

由于函数中的this只能去读取到上一级的作用域,所以下面要货物内部需要这样写

如果用bind,可以换成另一种写法,函数getName绑定当前对象

var func = {
  name: 'hello',
  a: function() {
    var getName = function() {
      console.log(this.name);   //hello
    }
    getName.bind(this)();   //等同于getName.call(this) 
  },
}
func.a(); 

getName.bind(this)()与 getName.call(this)结果一致,可得出bind返回的是函数,而call会直接执行。

下面的例子,print.bind({a:1},2)第一个参数是{a:1}, print函数绑定到了对象{a:1}上,所以this.a = 1,此时第二个参数2,传参到了b上,然后返回函数fn。调用fn继续传参,依次c=3, d = 4

function print(b,c,d){
    console.log(this.a);    // 1
    console.log(b,c,d);     // 2, 3, 4
    console.log(arguments); // [Arguments] { '0': 2, '1': 3, '2': 4, '3': 5 }
}

var fn = print.bind({a:1},2)
fn(3, 4, 5)

作用域 

块级作用域(私有作用域,ES5)

与一些语言不同,JS的for循环中定义的var变量,可在for循环结束后使用。匿名函数可以模仿块级作用域来避免此问题

for(var i=0; i<=5; i++){
  var a = 10;
}
console.log(i); //6
console.log(a); //10

//匿名函数
var func = function(){
  for(var i=0; i<=5; i++){
    var a = 10;
  }
}
func();
console.log(i); //出错
console.log(a); //出错

私有变量

任何函数中定义的变量,都可以认为是私有变量如果要访问函数内的变量,可以利用闭包

能够访问私有变量和私有函数的方法称为特权方法。

function obj(){
  let privateName="lhf";
  function privateFunc(){
    return false;
  }
  //特权方法:构造函数
  this.getName = function(){
    return privateName;
  }
}
let person = new obj();
console.log(person.getName());//lhf

闭包

什么是闭包

闭包是指有权访问另一个函数作用域变量的函数,能够访问自由变量的函数

表现形式:

  • 闭包是一个函数,而且存在于另一个函数当中
  • 闭包可以访问到父级函数的变量,且该变量不会销毁

闭包的作用

1.避免全局污染

利用闭包内的局部变量与自由变量,实现独立的计数器

function Add(){
    var a = 10;
    return function(x){
        a += x;
        return a;
    };
};
let numa = Add();
console.log(numa(5));

 2.实现单例模式

var aClass = function(){};
var getClassA = function(){
    let obj;
    return function(){
        if(obj){
            return obj;
        }
        else{
            obj = new aClass();
        }
        return obj;
    };
}();
let objA = getClassA();
let objB = getClassA();
console.log(objA === objB);  // true

this(多方面)

以下内容在html中运行

1.在普通方法中,this指向windows

2.对象中的this

对属性调用,this指向windows

var age = 18;
var obj = {
  name: "liu",
  myAge: this.age, // this指向windows
};
console.log(this); //widows
console.log(obj.myAge); //18

对函数调用,this指向该对象

var age = 18;
var obj = {
  name: "liu",
  myAge: this.age,     
  myFun: function () {
    console.log(this);                       //{name: "liu", myAge: 18, myFun: ƒ}
    console.log(this.name + ";" + this.age); //liu;underfined
  },
};
let name = obj.myFun(); //obj调用,this指向obj

 3.构造函数中的this,this指向构造函数的实例

构造函数生成实例的过程

创建一个新对象;将this指向这个新对象;执行构造函数中的代码,为新对象添加属性;返回新对象

4.call() 和 apply() 方法可以将 this 引用到任何对象

let a = {
    name: 'a'
}
let b = {
    name: 'b',
    getName: function(){
        console.log(this.name);
    }
}
b.getName.call(a);  //a

5.闭包

       每个函数在调用时,会自动获取两个变量:this和argument。内部函数在搜索这两个变量时,只会搜索到活动对象为止。类似于下面的例子,当执行return this.name时,只会在getName的匿名函数内搜索。

var objectName = {
  name: 'lhf',
  getName: function(){
    return function(){
      return this.name;
    }
  }
}
console.log(objectName.getName()());  //undefined

 进行下面的修改,就能返回想要的值了

6.箭头函数中的this

参考箭头函数

es6箭头函数会往外层搜索this指针,且不能改变指向

7.事件绑定中的this

事件绑定共有三种方式:行内绑定、动态绑定、事件监听

行内绑定:方法是全局的,所以this指向window

<body>
    <div onclick="clickEvent(this, event)">点我</div>
</body>
<script>
//方法是全局的,所以this指向window
function clickEvent(thisDom, event){
    console.log(thisDom); //该节点
    console.log(event);   //MouseEvent事件
    console.log(this);    //windows
}
</script>

动态绑定与事件监听:方法是绑定到元素上,所以this指定的是该元素


<body>
    <div id="button1" type="button" ></div>
</body>
<script>
var btn = document.getElementById('button1');
//方法是绑定到元素上,所以this指定的是该元素
btn.onclick = clickEvent;
function clickEvent(){
   console.log(this.id); //button1
   console.log(this);    // <div id="button1" type="button" ></div>
}
</script>

内存管理

内存管理 - JavaScript | MDN

内存分配

js在定义变量时就完成了内存的分配,普通的类型一般放在栈内存里,对象放在堆内存里,在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址

声明一个变量,就分配一些内存,然后定时进行垃圾回收,这里的垃圾回收只是近似的完成。

内存溢出

程序运行需要的内存超过了剩余的内存时, 就会抛出内存溢出的错误

内存泄漏

不再用到的内存,没有及时释放,就叫做内存泄漏

闭包泄漏

全局变量一直处在内存中

定时器

setInterval和setTimeout

事件监听?

当页面中元素被移除或替换时,若元素绑定的事件仍没被移除

垃圾回收

引用计数算法

当没有对象引用到此对象,引用计数为0,这时垃圾回收机制就会自动释放内存

缺点:遇到循环引用的情况,引用计数始终大于等于1,无法进行内存释放

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o 引用 o2
  o2.a = o; // o2 引用 o

  return "azerty";
}
f();

标记-清楚算法

这个算法的特点是寻找“对象是否可以获得” 而不是 “对象是否被引用”

主要思想:垃圾回收器从全局对象(类似于根)开始,找所有从全局对象开始的引用对象,再一次从找的的对象向下查找。垃圾回收器将找到所有”可获得的对象“和”所有不能获得的对象“。

AJAX

AJAX 创建 XMLHttpRequest 对象 | 菜鸟教程

 Asynchronous JavaScript and XML(异步的JavaScript和XML)

XHR(XMLHttpRequest)

优点:在不重新加载页面的情况下,可以与服务器交换数据并更新部分页面内容(网页实现异步更新);会利用浏览器缓存减少数据交互(IE独有特点,有数据更新的交互需要清除缓存)

方法

open(method, url, async)  async:true,异步;false,同步

send(string) string:仅用于post请求 ;将请求发送给服务器

setRequestHeader(header,value):向请求添加http头部信息

XMLHttpRequest.setRequestHeader() - Web API 接口参考 | MDN

onreadystatechange事件

async: true需要用到onreadystatechange事件,主要用于就绪状态时执行的函数,类似回调函数;

readyState:每当readyState改变时就会触发onreadystatechange事件;

  • 0: 请求未初始化
  • 1: 服务器连接已建立
  • 2: 请求已接收
  • 3: 请求处理中
  • 4: 请求已完成,且响应已就绪
if (window.XMLHttpRequest) {
  //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
  xmlhttp = new XMLHttpRequest();
} else {
  // IE6, IE5 浏览器执行代码
  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("GET", "http://118.178.197.33:8001/recruit_web/get_tags/", true);
xmlhttp.send();
xmlhttp.onreadystatechange = function () {
        console.log(xmlhttp);
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
          document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
        }
      };

AJAX的工作原理 

ajax会创建一个XHR对象,该对象向服务器发送请求,当该对象的属性readyState改变时,会触发回调函数onreadystatechange函数。

IE浏览器

get请求默认缓存,解决方法:

1.get请求添加时间戳
2.If-Modified-Since:浏览器缓存记录的该文件的最后服务器修改时间,如果时间不一致,就返新的内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。
Cache-Control:任何节点都不能直接使用缓存
xmlhttp.setRequestHeader("If-Modified-Since","0");  
xmlhttp.setRequestHeader("Cache-Control","no-cache"); 

其他浏览器

默认不缓存

浏览器缓存机制

前端必须要懂的浏览器缓存机制 - 牧云流 - 博客园

     浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识

     浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中,控制强制缓存的字段分别是Expires和Cache-Control

jQuery的AJAX

对原生XHR的封装,除此以外还增添了对JSONP的支持

$.get(URL,callback

$.post(URL,data,callback

 $.ajax({})

jQuery.ajax中success 和complete 区别 - alice-you - 博客园

url

type:请求方式,默认get

data:会自动将对象转化为字符串;GET 请求中将附加在 URL 后;POST请求中是请求的参数

async:true 异步;false 同步

cache:true 缓存; false 不缓存

context: object, 为了让ajax的回调函数的this指向这个对象,如果不设置这个参数,那么this指向的是此次请求时传递的 options 参数(请求传递的所有参数对象)

beforeSend(XHR):发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头

error(XHR, textStatus):请求失败的回调函数; XHR:XMLHttpRequest 对象 textStatus:error

success(result, textStatus):请求成功的回调函数; result :返回的数据,json字符串  textStatus:success

complete(XHR, textStatus):请求完成后的回调函数,无论成功失败都会调用;textStatus:error|success  

option参数

 $.ajax({
        url: "http://118.178.197.33:8001/recruit_web/chart_data/",
        type:'POST',
        context: document.getElementById('myDiv'),
        data:{tag_id:0},
        beforeSend(XMLHttpRequest){
           //发送请求前,修改XMLHttpRequest
        },
        success: function (result,  textStatus) {
          console.log(this);
          $(this).text("done");
        },
        error: function (XMLHttpRequest, textStatus) {
          $(this).text("error");
        },
        complete:function(XMLHttpRequest, textStatus){
          console.log('complete');
        }
)

axios

axios中文文档|axios中文网 | axios

axios是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,本质上也是对原生XHR的封装

优点

1.支持Promise api

2.自动转换JSON数据

3.拦截请求和响应

post请求

要说明Content-Type,且传参必须为规定字符串,可用qs.stringify函数,html需要引入

let param = "tag_id=0";
axios
  .post('http://118.178.197.33:8001/recruit_web/chart_data/', param, 
        {'headers': {'Content-Type': 'application/x-www-form-urlencoded'}})
  .then(function (response) {
    console.log(response); //object
    data = response.data;
  })
  .catch(function (error) {
    console.log(error); //object
  });

如果参数格式不正确,可能会出现下面这种错误,无value

执行多个并发请求

axios
  .all([get_chart(), ...])
  .then(function (response) {
    //全部成功返回
    console.log("success");
  })
  .catch(function (error) {
    console.log("error");
  });

JSON.stringify()与qs.stringify()

qs.stringify:把对象转化为url形式

qs.parse: 解析url为对象

const Qs = require('qs');
param = Qs.stringify({tag_id: 0, name:1})
console.log(param); //tag_id=0&name=1

JSON.stringify: 把对象转化为json字符串

JSON.parse: 字符串转化为对象

传参编码

decodeURIComponent :解码导航栏的字符

JS的节流和防抖

优点:性能优化

节流:事件触发执行了一次之后,在规定时间内不再执行

例子:点击事件

let lastTime = 0;
let dayObj = new Date();
function clickStart() {
  let nowTime = new Date().getSeconds();
  if ((lastTime && nowTime - lastTime > 2) || !lastTime) {
    if (clickEvent()) {
      lastTime = nowTime;
      nowTime = new Date().getSeconds();
    }
  }
}

防抖:触发事件的时候不立即执行,给定一定的时间,如果在这个时间间隔内再次触发了该事件,那么取消之前的事件,在当前事件重新计时

例子: 输入框搜索:设定无触发一个时间段后执行搜索

var timeId;
function clickStart() {
  console.log("start" + timeId);
  if (timeId) {
    console.log("clear");
    clearTimeout(timeId);
    timeId = null;
  }
  timeId = setTimeout(clickEvent, 2000);
  console.log(timeId);
}

 var、 let、 const(ES6

var: 变量提升

let:暂时性死区;不允许在相同作用域内,重复声明同一个变量;为 JavaScript 新增了块级作用域;

const:暂时性死区;不允许在相同作用域内,重复声明同一个变量;声明的变量不准改变值;

变量提升:即变量可以在声明之前使用,值为undefined

暂时性死区:使用let命令声明变量之前,该变量都是不可用的

声明的变量不准改变值:当被赋值简单类型的数据(数值、字符串、布尔值),值就被保存在变量指向的那个内存地址,,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。

const a = [];
a.push('Hello');

块级作用域 

如果没有块级作用域(ES5):1.内层变量会覆盖外层变量 2:用来计数的局部变量泄露为全局变量

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
  else{
     //tmp覆盖全局变量
  }
}
f(); // undefined

var s = 'hello';
for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

 ES6允许块级作用域任意嵌套,就是多层函数时,每一层都是一个单独的作用域

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

解构

数组

按照对应位置,对变量赋值,如果解构不成功,变量的值就等于undefined;解构赋值允许指定默认值,只有当一个数组成员严格等于undefined,默认值才会生效。

对象

与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值;

可以很方便地将现有对象的方法,赋值到某个变量:

let { log, sin, cos } = Math;

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined

扩展运算符 

对象

1.与Object.assign相同,浅copy对象的第一层数据结构

let obj1 = { a: 1, b: [1] };
console.log({ ...obj1 });   //带上大括号,{ a: 1, b: [ 1 ] }
// 等价
console.log(Object.assign({}, obj1));


let obj1 = { a: 1, b: [1], c: { nickName: "d" } };
let obj2 = { ...obj1 };
console.log(obj2);   //{ a: 1, b: [ 1 ], c: { nickName: 'd' } }

obj1.a = 5;         //浅coy,赋值一层数据结构
console.log(obj2);  //{ a: 1, b: [ 1 ], c: { nickName: 'd' } }

obj1.b.push(2);     //赋值的指针对象
console.log(obj2);  //{ a: 1, b: [ 1, 2 ], c: { nickName: 'd' } }

obj1.c.nickName = 5; //赋值的指针对象
console.log(obj2);   //{ a: 1, b: [ 1, 2 ], c: { nickName: 5 } }

数组

1.序列化

let obj1 = [1, 2, 3, 4];
console.log(...obj1); //1 2 3 4, 序列化

2.与解构赋值结合,注意扩展运算符只能放在最后一位

let [x, y, ...z] = obj1;
console.log(x); //1
console.log(y); //2
console.log(z); //[ 3, 4 ]

3.任何 Iterator 接口的对象,都可以用扩展运算符转为真正的数组

箭头函数

下面两种写法效果相同,如果参数不是一个,就用()括起来

let v1 = x => x * x;
console.log(v1(2));  //4

let v2 = function(x){
    return x*x;
}
console.log(v2(2))  //4

this作用域

箭头函数的this ,this总是指向词法作用域。箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this

//JS函数中this只能获取上一级作用域
var obj1 = {
    name: 'obj1',
    getName: function(){
        var fn = function(){
            console.log(this.name);  //undefined
        }
        return fn();
    }
}
obj1.getName();  

//箭头函数
var obj1 = {
    name: 'obj1',
    getName: function(){
        var fn = () => {
            console.log(this.name);  //obj1
        };
        return fn();
    }
}
obj1.getName();  

箭头函数与function的区别

箭头函数没有自己的this,在箭头函数中使用的this时,是沿着作用域链寻找,找到最近的this

箭头函数获取 arguments 

剩余参数表示法

let v1 = (...args) => {
    console.log(args); //[2]
    return args[0] * args[0];
}
console.log(v1(2));  //4

Iterator遍历器

原理

作用:为各种不同的数据结构提供统一的访问机制;使得数据结构的成员能够按某种次序排列;

Iterator主要供for....of 使用;

使用的对象:Array、Map、Set、String、函数的 arguments 对象

遍历过程:

        1.创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象

         2.不断调用指针对象的next方法,直到它指向数据结构的结束位置

每次调用返回一个包含valuedone两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束

//模拟next方法的例子
function makeIter(array){
  var index = 0;
  return {
    next: function(){
      return index<array.length ? 
      {value: array[index++], done: false} :
      {value: array[index], done: true};
    }
  }
}

var tmp = makeIter([0, 1, 2]);
console.log(tmp.next());  //{ value: 0, done: false }

一种数据结构具有Symbol.iterator属性,就被认为是可遍历的。不用任何处理,就可以被for...of循环遍历。原生具备 Iterator 接口的数据结构如下:Array、Map、Set、String等

for...in与for...of

  • for...in遍历数组的索引,  for...of遍历数组的值,且不会输出foo
  • for...in遍历Object的key, for...of遍历Object对象出错
let arr = [3, 5, 7];
arr.foo = 'hello';
console.log(arr);
for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo"
}

for (let i of arr) {
  console.log(i); //  "3", "5", "7"
}
let a = new Object();
a.name = 'kkk';
console.log(a);       //{ name: 'kkk' }

for (let i in a){     
    console.log(i);   //name
}
for (let i of a){     //出错,应该是因为没有、iterator接口
    console.log(i);
}

字符串(ES6)

  • includes():返回布尔值,表示是否找到了参数字符串。
  • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
  • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
  • 这三个方法都支持第二个参数,表示开始搜索的位置。
let s = 'Hello world!';

s.startsWith('world', 6) // true
s.endsWith('Hello', 5) // true
s.includes('Hello', 6) // false

repeat方法返回一个新字符串

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

padStart()用于头部补全,padEnd()用于尾部补全

'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'

'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'

trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

const s = '  abc  ';

s.trim() // "abc"
s.trimStart() // "abc  "
s.trimEnd() // "  abc"

方法replace()只能替换第一个匹配;replaceAll()方法,可以一次性替换所有匹配。

'aabbcc'.replace('b', '_')
// 'aa_bcc'

'aabbcc'.replaceAll('b', '_')
// 'aa__cc'

set

set接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数

add()、delete()、size、 clear()、has(value)

let a = new Set([1,2,3,3,5]);
console.log(a);
a.add(8);
a.delete(1);
console.log(a);

set转化为数组,两种方法

let a = new Set([1,2,3,3,5]);
console.log([...a]);        //[ 1, 2, 3, 5 ]
console.log(Array.from(a)); //[ 1, 2, 3, 5 ]

遍历操作

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员

WeakSet

与set不同的两点

  1. 元素只能是对象
  2. 如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中

map

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。

let a = {1:2};
console.log(a); //{ '1': 2 }

Map数据结构也是键值对的集合,但是key可以说多种数据结构。如果键值是引用类型,键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键

基本操作

set(key, value)、get(key)、delete(key)、has(key)、size

let a = new Map();
a.set(1, 'o');     //添加键值对
a.set(2, 'k'); 
console.log(a.get(1));
a.delete(1);          // o
console.log(a.has(2)) //true
console.log(a);       // Map(1) { 2 => 'k' }

其他数据类型转化为map

事实上,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构(详见《Iterator》一章)都可以当作Map构造函数的参数

let a = new Set([['a', 1], ['b', 2]]);
let b = new Map(a);
console.log(b);  //Map(2) { 'a' => 1, 'b' => 2 }

遍历方法

因为a.keys()、a.values()是Iterator类型,所以需要用for...of遍历

  • Map.prototype.keys():返回键名的遍历器。
  • Map.prototype.values():返回键值的遍历器。
  • Map.prototype.entries():返回所有成员的遍历器。
  • Map.prototype.forEach():遍历 Map 的所有成员。
let a = new Map();
a.set(1, 'aa');
a.set('k', 'bb');
console.log(a);
console.log(typeof a);          //object, typeof检测不出map类型
console.log(a instanceof Map);  //true
console.log(a.keys());    //[Map Iterator] { 1, 'k' }
console.log(a.values());  //[Map Iterator] { 'aa', 'bb' }
console.log(a.entries()); //[Map Entries] {[ 1, 'aa' ], [ 'k', 'bb' ]}

// 因为a.keys()、a.values()是Iterator类型,所以需要用for...of遍历
for (let i of a.keys()){
    console.log(i, a.get(i));
}
// 1 aa
// k bb
for (let i of a.entries()){
    console.log(i);
}
// [ 1, 'aa' ]
// [ 'k', 'bb' ]

WeakMap

与map有两点不同

  • WeakMap只接受对象作为键名;
  • WeakMap的键名所指向的对象,不计入垃圾回收机制,需要手动删除key,value;

只有四个方法可用:get()set()has()delete()

symbol

ES6提供的新数据类型,代表独一无二的值;

symble函数前不能使用new命令;

smbol的参数,只是对symbol对象的描述;

const sym = Symbol('foo');
console.log(sym.description); //foo

一个字符串作为参数,表示对 Symbol 实例的描述;

let a = Symbol('hahaha');
let b = a.toString();
console.log(b); //Symbol(hahaha)

一个对象作为参数,首先会调用该对象的toString()方法,把对象转化为字符串;

let a = {
    toString(){
        return 'asd'
    }
};
let b = Symbol(a);
console.log(b); //Symbol(asd)

symbol值作为属性名时,不能用点运算符,应为点运算符后的总是字符串

const sym = Symbol('foo');
let a = {};
a.sym = '1';
console.log(a[sym]); //undefined

 getOwnPropertySymbols,获取对象中属性值是sumbol的键名;

const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';

let symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols);  //[ Symbol(a), Symbol(b) ]

Symbol.for(str)

全局登记特性

可以获取同一个以str作为描述的symbol值,如果目前没有此值,就会重新创建一个;

优点:

  • 用于对象的属性名,就能保证不会出现同名的属性
  • 消除魔术字符串(在代码中多次出现的同一个字符串或数值)

异步编程

异步编程就是主线程生成一个子线程来完成任务,JS中的异步操作往往通过回调函数来实现异步任务的结果处理

下面这个函数执行后,会产生一个子线程,子线程会等待 3 秒,然后执行回调函数 "print";在 setTimeout 函数执行之后主线程并没有停止,所以会立即输出下面的语句

function print() {
  console.log("三秒后输出")
}
setTimeout(print, 3000);
console.log('主线程继续向下执行,立即输出')


主线程继续向下执行,立即输出
三秒后输出

(todo)

Promise(ES6)

  • resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列;
  • resolve 和 reject 并不能够使起始函数停止运行,别忘了 return。
  • resolve 代表一切正常,reject 是出现异常时所调用的
new Promise(function (resolve, reject) {
    var a = 0;
    var b = 1;
    if (b == 0) reject("Divide zero");
    else resolve(a / b);
}).then(function (value) {
    console.log("a / b = " + value);
}).catch(function (err) {
    console.log(err);
}).finally(function () {
    console.log("End");
});

 then 块如何中断

then 块默认会向下顺序执行,return 是不能中断的,可以通过 throw 来跳转至 catch 实现中断

什么时候适合用 Promise 而不是传统回调函数

当需要多次顺序执行异步操作的时候

Promise.all

参数:1.数组 2.具有 Iterator 接口,且返回的每个成员都是 Promise 实例

状态是由所有参数决定:当所有Promise 实例都返回成功,才成功;如果有一个返回失败,则状态为失败

  • Promise 内部的错误不会影响到 Promise 外部的代码
  • catch处理 reject返回 和 then运行时发生的错误

ES6 入门教程

Generator

两个特征:function与函数名中间有一个星号;函数内部使用yield表达式

generator的调用方法与普通函数一样,返回的是一个指向内部状态的指针对象,需要调用next()才会执行

    var run = function* () {
      alert(
        "这是一种声明函数的方式,左边是一个变量,右边是一个函数的表达式,意思就是把一个匿名的函数表达式赋值给了变量myfun,只是声明了一个变量指向了一个函数对象。"
      ); 
      yield 1;
    };

    console.log(run().next()); // 返回Iterator

async

把generator的*和yield换成了async和await,像普通函数一样调用就可以

返回Promise对象,可以使用then方法添加回调函数,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

    var run = async function () {
      alert(
        "这是一种声明函数的方式,左边是一个变量,右边是一个函数的表达式,意思就是把一个匿名的函数表达式赋值给了变量myfun,只是声明了一个变量指向了一个函数对象。"
      ); 
      await 1;
    };

    console.log(run());//返回Promise对象

    // async 写法
    // 函数声明:普通函数
    async function foo(){}

    // 函数表达式:匿名函数
    const foo = async function (){}

    // 对象的方法
    let obj = {async foo(){}};
    obj.fool().then(...)

    // 箭头函数:去掉了function,多了个箭头
    const foo = async() => {};

事件

事件冒泡

事件由子元素向父元素传递

点击dom3,触发事件dom3、dom2、dom1

阻止冒泡,在子元素dom3的事件中添加e.stopPropagation(),添加后指挥执行dom3事件

<template>
  <div class="home">
   <div id="d1">
      dom1
      <div id="d2">
        dom2
        <div id="d3">dom3</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "",
  mounted() {
    let dom1 = document.getElementById("d1");
    let dom2 = document.getElementById("d2");
    let dom3 = document.getElementById("d3");

    dom1.addEventListener(
      "click",
      function (e) {
        e.stopPropagation();
        console.log("dom1-----", e);
      },
      true //默认false, 冒泡阶段执行
    );

    dom2.addEventListener(
      "click",
      function (e) {
        // e.stopPropagation();
        console.log("dom2-----", e);
      },
      true
    );

    dom3.addEventListener(
      "click",
      function (e) {
        // e.stopPropagation();
        console.log("dom3-----", e);
      },
      true
    );
  },
};

</script>
<style lang="sass" scoped>
#d1, #d2, #d3{
  display:inline-block;
}
#d3{
  width: 100px;
  height: 100px;
  background-color: pink;
}
#d2{
  padding: 20px;
  background-color: #fff;
}
#d1{
  padding: 20px;
   background-color: green;
}
</style>

事件捕获

事件从父元素向子元素传递

点击上述dom3,触发dom1、dom2、dom3

阻止捕获,在父元素dom1的事件中添加e.stopPropagation(),添加后指挥执行dom1事件

事件委托(事件代理)

事件委托是利用事件冒泡原理实现的,委托它们父级代为执行事件。

事件处理

1.DOM0级事件处理

将一个函数赋值给一个事件处理属性(元素.click="某函数")

this指向当前所属元素的作用域

  <body>
    <div id="button1" type="button" ></div>
</body>
<script>
  var btn = document.getElementById('button1');
  btn.onclick = clickEvent;
  function clickEvent(){
    console.log(this.id); //button1
    console.log(this);    // <div id="button1" type="button" ></div>
  }
</script>

2.DOM2级事件处理

addEventListener 添加事件函数  removeEventListener 移除事件函数

两个方法需要接受相同的参数(事件名,事件函数,布尔值),例(“click", handle, false)

false:表示在冒泡阶段调用事件  true:表示在捕获阶段调用事件

this指向当前所属元素的作用域

  <body>
    <div id="button1" type="button" ></div>
</body>
<script>
  var btn = document.getElementById('button1');
  btn.addEventListener('click', clickEvent, false);
  function clickEvent(){
    console.log(this.id); //button1
    console.log(this);    // <div id="button1" type="button" ></div>
  }
</script>

3.IE事件处理程序

attachEvent 添加事件函数(冒泡阶段)  detachEvent 删除事件函数

两个方法需要接受相同的参数(事件名,事件函数),例(“onclick", handle)

支持的浏览器有IE和Opera

<body>
    <div id="button1" type="button" ></div>
</body>
<script>
  var btn = document.getElementById('button1');
  btn.attachEvent("onclick", clickEvent);
function clickEvent(){
    console.log(this.id); //button1
    console.log(this);    //widow
}

</script>

4,DOM中的事件对象

DOM0和DOM2级处理事件都会将event对象传入到处理程序中

event.target , 是click的真实目标,实际点击的元素

event.currentTarget 和 this,指向事件处理程序绑定的节点

event.stopPropagation() 立即停止事件在DOM层的传播,即取消进一步的事件冒泡或捕获

event.preventDefault() 阻止特定事件的默认行为 ,比如a标签的href

5.IE中的事件对象

DOM0级的方式添加事件,event是作为window的属性存在,即event=window.event;

attachEvent()方式添加事件,会有一个event作为参数传入处理函数

event.cancelBubble = event.stopPropagation, 立即阻止事件的传播,默认false;

event.returnValue = event.preventDefault, 取消事件的默认行为,默认true;

event.select = event.target, 指向真实操作的节点

event.type , 被触发事件的类型

事件类型

load 事件加载之后触发的load事件

<body>
    <img src="http://118.178.197.33:8001/media/mine/1.jpg" alt="" onload="onloadEvent(event)">
</body>

<script>
    function onloadEvent(event){
        console.log(this);       //window
        console.log(event.type); //load
    }
</script>

JS的异步模式

为了解决程序顺序执行,遇到操作耗时的问题,可以用以下几种异步模式解决

回调函数

可以把耗时操作后的操作写到回调函数中. func1、func2需要按照顺序执行,func3不需要等待 func1、func2执行完毕。setTimeout不会中断主线程,所以主线程会先执行func3,再执行func1、func2.

//情况一
let a;
function func1(f2) {
  //耗时2s操作
  console.log('func1');
}

function func2() {
  console.log("func2");
}

function func3() {
  console.log("func3");
}

function main() {
  console.log("begin");
  func1();
  func2();
  func3();
  console.log("end");
}

main();

//情况二
let a;
function func1(f2) {
  //耗时2s操作
  setTimeout(function () {
    console.log('func1');  
    f2();
  }, 2000);
}

function main() {
  ...
  func1(func2);
  ...
}

main();

/*
情况一:
begin
func1
func2
func3
end

情况二:
begin
func3
end
func1
func2
*/

事件监听

任务的执行不取决于代码的顺序,而取决于某个事件是否发生

Promises异步函数

异步

怎样解决跨域问题 

1.jsonp

就是利用

2.cors

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源

3.使用nginx反向代理实现跨域,是最简单的跨域方式

反向代理:接收浏览器的请求发送给服务器,并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器

eval

作用:把字符串转化成js代码并运行

eval("var age = 10");
console.log(age); //10

如何理解前端模块化

前段模块化就是把部分代码编写为一个模块,可以重复使用

手动实现bind函数

Function.prototype.bind2 = function(obj){
  //obj:是第一个参数,this
  self = this;
  var args = Array.prototype.slice.call(arguments, 1);//对象转数组,从下标为1的元素开始截取(slice功能)
  console.log(this)       //[Function: print]
  console.log(args);      //[ 2 ]
  return function(){
      // innerArgs: bind绑定后的传参
      var innerArgs = Array.prototype.slice.call(arguments);
      console.log(innerArgs); //[ 3, 4, 5 ]
      // 第一次传参与第二次传参结合
      var finalArgs = args.concat(innerArgs);
      return self.apply(obj, finalArgs);
  }
}

function print(b,c,d){
  console.log(this.a);    // 1
  console.log(b,c,d);     // 2, 3, 4
  console.log(arguments); // [Arguments] { '0': 2, '1': 3, '2': 4, '3': 5 }
}

var fn = print.bind2({a:1},2)
fn(3, 4, 5)

实现JS所有对象的深度克隆

function copy(obj) {
  let typeS = typeof obj;
  if (
    ["string", "number", "undefined", "boolean", "symbol", "string"].includes(
      typeS
    )
  ) {
    return obj;
  }
  // null
  else if (!obj && typeof obj != "object") {
    return obj;
  }
  // 无法实现对象中方法的copy   
  else {
    return JSON.parse(JSON.stringify(obj));
  }
}

export

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值