目录
JSON.stringify()与qs.stringify()
add()、delete()、size、 clear()、has(value)
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>
内存管理
内存分配
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是一个基于 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
方法,直到它指向数据结构的结束位置
每次调用返回一个包含value
和done
两个属性的对象。其中,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不同的两点
- 元素只能是对象
- 如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 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运行时发生的错误
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));
}
}