1、创建对象的3种方式
//字面量创建对象
var obj1 = { name = "zhang"};
var obj2 = new Object({ name: "li" });
//构造函数创建对象
var O = function () {
this.name = "wang";
}
var obj3 = new O();
//Object.create创建对象
var p = { name: "zhao"};
var obj4 = Object.create(p);
2、JS的数据类型
数据类型分类:
1、基本数据类型:Undefined、Boolean、String、Number、Null、Symbol(ES6新定义)
基本类型存储在栈内存种,数据大小确定,内存空间大小可以分配,按值存放,所以可直接访问。
2、引用类型(Object对象):对象、数组、函数、日期、正则
引用类型存放在堆中,引用类型存放在堆内存中,变量实际上是一个存放在栈内存中的指针,这个指针指向堆内存中的地址,每个空间的大小不一样,要根据情况进行特定的分配。
数据类型的判断:
1、typeof:返回的是一个数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function等7种数据类型,但不能判断null、array等。
2、instanceof:用来判断A是否为B的实例,A instanceof B,返回Boolean值。instanceof用来测试一个对象在其原型链种是否存在一个构造函数prototype属性,但它不能够检测null和undefined。
3、Object.prototype.toString.call():最准确最常用,所有数据类型都能够判断。
3、原型链
1、JavaScript种的继承是通过原型链来体现的。
2、每一个对象都有一个__proto__属性,指向构造函数的prototype。
3、访问一个对象的属性时,现在基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
4、DOM事件
1、DOM事件级别:
//DOM0
element.onclick = function(){}
//DOM2
element.addEventListener('click',function(){
//false为冒泡阶段,true为捕获阶段
},false);
//DOM3
element.addEventListener('keyup',function(){
},false);
2、事件流:用户与浏览器当前页面的交互过程,可以分为三个阶段(捕获阶段、目标阶段、冒泡阶段)
3、事件模型:事件捕获、事件冒泡
5、事件循环(Event Loop)
JavaScript是一门单线程的执行语言,处理任务的时候是一件一件往下处理。
浏览器在执行js代码过程中维护了一个执行栈,每个方法都会进入执行栈执行之后出栈。于此同时,浏览器又维护了一个消息队列,所有的异步方法,在执行结束后都将回调方法塞入消息队列中,当所有执行栈中的任务全部都执行完毕后,浏览器开始往消息队列中寻找任务,先进消息队列的先执行。
宏任务和微任务
宏任务:js同步执行的代码块:setTimeout、setInterval、XMLHttprequest等
微任务:promise、process.nextTick(node环境)等
执行栈中执行的任务都是宏任务,当宏任务遇到Promise的时候会创建微任务,当Promise状态fulfill的时候塞入微任务队列。在一次宏任务完成之后,会检查微任务队列有没有需要执行的任务,有的话按照顺序执行微任务中的所有任务。之后再开始执行下一次宏任务。具体步骤如下:
1、执行主代码块
2、若遇到Promise,把then之后的内容放进微任务队列
3、一次宏任务执行完成,检查微任务队列有无任务
4、有的话执行所有微任务
5、执行完毕后,开始下一次宏任务。
6、闭包
闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。通俗来说,闭包就是能够读取其他函数内部变量的函数。
变量的作用域:全局变量和局部变量(函数内部可以直接读取全局变量,函数外部自然无法读取函数内的局部变量)
闭包的用途:一个是可以读取函数内部的变量,另一个是可以让这些变量的值始终保持在内存中。
7、Promise
promise就是将异步任务队列化,将多个异步任务按照顺序输出,同时用链式调用解决回调地狱(函数作为参数层层嵌套)的问题。
用法:
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('我是异步返回的数据')
}, 1000);
});
p.then(data => {
console.log(data);
});
promise规范:
- 存在三个状态:等待态(pending),执行态(fulfilled),失败态(rejected)
- 初始态为等待态,可以转化为执行态和失败态
- 执行态不可转化为其他状态,且必须有一个不可变的终值(value)
- 失败态不可转化为其他状态,且必须有一个不可变的原因(reason)
- 必须提供一个then方法,以供访问其当前值,终值及原因
- then方法提供两个参数:onFulfilled和onRejected
- onFulfilled和onRejected如果不是函数类型,必须忽略
- 如果executor执行报错,直接执行reject
- 不同的promise可以相互套用
8、this指向问题
this的指向不是在函数定义时确定的,而是在函数调用时确定
this默认情况下指向window,严格模式下为undefined
隐式绑定:即this指向距离其最近的调用者
// this的隐式绑定
function fn() {
console.log(this);
}
var obj = {
name: 'zhang',
fn: function () {
console.log(this);
}
}
fn(); // window
obj.fn(); // {name: "zhang", fn: ƒ}
显式绑定(强制修改): 在JavaScript中,有一些函数可以强制修改this的指向,如call, apply, bind等。
- call, apply, bind 中的this会指向传入的第一个参数
- 如果这些函数调用时没有传入参数,则指向默认对象(window或undefined)
构造函数中的this指向: 构造函数中的this指向该函数创建的实例对象
箭头函数中的this指向: 因为箭头函数本身并不存在this,是由其父级作用域继承而来。箭头函数中的this无法通过bind、call、apply进行修改。
立即执行函数中的this指向: 立即执行函数中的this永远指向window
9、ES6的新特性
1、let和const命令的出现
- let和const区别:const定义一个只读常量,一旦声明变量,就必须立即初始化,不能留到以后赋值且不可改变。
- 不存在变量提升,声明的变量一定要在声明后使用,否则报错
- 不允许重复声明变量,不允许在相同作用域内,重复声明同一个变量
- 块级作用域,ES6之前只有函数作用域与全局作用域,一个大括号即一个块级作用域
- 声明的变量不在属于window
2、解构赋值
解构赋值是对赋值运算符的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
3、函数的扩展
箭头函数、REST参数
4、数组的扩展
扩展运算符:
用途:(1)复制数组、(2)合并数组、(3)函数的rest参数
5、对象上的扩展
1、可遍历性:for...in
、Object.key(obj)
2、super关键字的增加
3、新增方法Object.is()
(对象的比较)、Object.assign()
(对象的拷贝克隆合并)
6、set和map结构
Set(集合)[类数组]: 它类似于数组,但是成员的值都是唯一的,没有重复的值
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i); // 2 3 5 4
}
// 去除数组的重复成员
let array = [1,2,1,4,5,3];
[...new Set(array)] // [1, 2, 4, 5, 3]
属性
size:返回字典所包含的元素个数
操作方法
- add(value):添加某个值,返回 Set 结构本身。
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- has(value):返回一个布尔值,表示该值是否为 Set 的成员。
- clear():清除所有成员,无返回值。
- size: 返回set数据结构的数据长度
Map (字典)[类对象]: 它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。
集合和字典的区别:
共同点:集合、字典可以存储不重复的值
不同点:集合是以[值,值]的形式存储元素,字典是以[键,值]的形式存储
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
属性
size:返回字典所包含的元素个数
操作方法
- set(key, val): 向字典中添加新元素
- get(key):通过键值查找特定的数值并返回
- has(key):如果键存在字典中返回true,否则false
- delete(key): 通过键值从字典中移除对应的数据
- clear():将这个字典中的所有元素删除
10、JS数组操作
- toString():
toString()
方法能够将数组转换为以逗号分隔的字符串。 - join(): join() 方法将所有数组元素组合成一个字符串。返回一个以参数为分隔符的字符串。
- concat: 将两个数组组合在一起,或者向数组中添加更多的元素项,然后返回一个新数组。
- push(): 将元素项添加到数组的末尾,并修改原始数组。
- pop(): 此方法删除数组的最后一项并返回。
- shift(): 此方法删除数组的第一项,并将它返回。
- unshift(): 此方法将一个项添加到数组的开头,并修改原始数组。
- splice(): 此方法通过添加、删除和插入元素来修改数组。
array.splice(index[, deleteCount, element1, ..., elementN])
Index
这里是删除数组中元素的起点deleteCount
是要从该索引中删除的元素数element1
,…
,elementN
是要添加的元素
注意: deleteCount 不包括范围内的最后一个索引
如果没有声明第二个参数,则将会从数组中删除从给定索引开始的所有元素
要添加项目,我们需要将 deleteCount 设置为零
- slice(): 此方法复制数组的给定部分,并将复制的部分作为新数组返回。 它不会改变原始数组。
array.slice(start, end)
let numbers = [1, 2, 3, 4]
numbers.slice(0, 3)
// returns [1, 2, 3]
- split(): 此方法用于字符串。它将一个字符串分成子串并将它们作为数组返回。
- indexOf(): 此方法在数组中查找项目,如果它被找到就返回索引,否则返回 -1
- lastIndexOf(): 这种方法的工作方式与 indexOf() 相同,只是它从右到左工作。它返回找到的最后一个索引
- filter(): 如果数组的项目符合某个条件,则此方法将会创建一个新数组。
- map(): 此方法通过操作数组中的值来创建新数组。
- reduce(): 此方法适用于计算总计的值。
let sum = [1, 2, 3, 5].reduce((acc, current) => {
return acc + current
}, 0)
11、深拷贝&浅拷贝
对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的。
浅拷贝的意思就是只复制引用,而未复制真正的值。
深拷贝就是对目标的完全拷贝,不像浅拷贝那样只是复制了一层引用,就连值也都复制了
目前实现深拷贝的方法不多,主要是两种:
- 利用 JSON 对象中的 parse 和 stringify
undefined、function、symbol 会在转换过程中被忽略
- 利用 递归 来实现每一层都重新创建对象并赋值
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
12、前端跨域的集中方式
- CORS跨域 (全拼 cross-origin resource sharing,意思是跨域资源共享)
注意: 所以要使用 CORS 进行跨域的话,必须注意客户端和服务器必须同时支持。
请求方法为三种方法之一:HEAD、GET、POST
HTTP的头信息不超出这几种字段:Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type
简单请求:浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
非简单请求:非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为 预检请求(preflight)。
- jsonp跨域
jonsp跨域的原理是通过动态创建script的标签的形式来实现跨域的,因为script不受同源策略的影响。但是jsonp只能接受get方法。