一、JavaScript的基本数据类型
1、 基本类型:字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。
2、 引用数据类型:对象(Object)、数组(Array)、函数(Function)。
Object是js中所有对象的父对象
新类型:Symbol
3、 JavaScript的基本类型和复杂类型是储存在哪里的?
基本类型储存在栈中,但是一旦被闭包引用则成为常住内存,会储存在内存堆中。
复杂类型会储存在内存堆中。
数据类型判断
1.typeof typeof 对于基本数据类型判断是没有问题的,但是遇到引用 数据类型(如:Array)是不起作用
2.instanceof 判断 new 关键字创建的引用数据类型 不考虑 null 和 undefined(这两个比较特殊)以对象字面量创建的基本数据类型
3.constructor 似乎完全可以应对基本数据类型和引用数据 类型 但如果声明了一个构造函数,并且把他的原型指向了 Array 的原型
4.Object.prototype.toString.call() 完美的解决方案
二、sessionstorage和localstorage的区别
localStorage:
生命周期是永久,这意味着除非用户显示在浏览器提供的 UI 上清除 localStorage 信息,否则这些信息将永远存在。存放数据大小为一般为 5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信
sessionStorage:
仅在当前会话下有效,关闭页面或浏览器后被清除。存 放 数据大小为一般为 5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务 器的通信。源生接口可以接受,亦可再次封装来对 Object 和 Array 有更好的支 持。
三、解构赋值
概念:
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称 为解构(Destructuring) 解构赋值,左右结构必须一样,使用左边定义的值,快速的取出数据中对应的 数据值,而且 定义和赋值必须放到一起,不然的话就会报错,取不出来数据值, 而且左边也必须是一个 js 存在数据结构不然的话也会报错,解构赋值的主要作 用还是,快速的让我们在数据中抓取出我们想要的数据
四、事件冒泡,事件捕获
什么是事件?
事件是文档和浏览器窗口中发生的特定的交互瞬间。
事件可能是用户在某些内容上的点击,鼠标经过某个特定元素或按下键盘 上的某些按键,事件还可能是 web 浏览器中发生的事情,比如说某个 web 页面 加载完成,或者是用户滚动窗口或改变窗口大小。
什么是事件流?
事件流描述的是从页面中接受事件的顺序,但有意思的是,微软(IE)和 网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流 是事件冒泡流(event bubbling), 而 Netscape 的事件流是事件捕获流(event capturing)。
事件冒泡和事件捕获的概念:
事件冒泡和事件捕获是描述事件触发事件时序问题的术语,事件捕获指的是 从 document 到触发事件的那个节点,也就是说自上而下的去触发事件,相反的, 事件冒泡是自下而上的去触发事件,绑定事件方法的第三个参数,就是控制事件 触发顺序是否为事件捕获,true 为事件捕获,false 为事件冒泡, jQuery 的 e.stopPropagation 会阻止冒泡,意思就是到我为止,我的爹和祖宗的事件就不要触 发了
第一种:事件冒泡
概念:
事件冒泡 IE 提出的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后 逐级向上传播到较为不具体的节点
执行顺序: p=>button=>div=>body
第二种:事件捕获
事件捕获流的思想是不太具体的 DOM 节点应该更早接收到事件,而最具体 的节点应该最后接收到事件,针对上面同样的例子,点击按钮,那么此时 click 事件会按照这样传播。
执行顺序:body=>div=>button=>p
五、for…in 迭代和 for…of 有什么区别
1、推荐在循环对象属性的时候,使用 for…in,在遍历数组的时候的时候使用 for…of。
2、for…in 循环出的是 key,for…of 循环出的是 value
3、注意,for…of 是 ES6 新引入的特性。修复了 ES5 引入的 for…in 的不足
4、for…of 不能循环普通的对象,需要通过和 Object.keys()搭配使用
六、谈谈this对象的理解
1.this是js的一个关键字,随着函数使用场合不同,this的值会发生变化。
2.但是有一个总原则,那就是this指的是调用函数的那个对象
3 this一般情况下:是全局对象Flobal。作为方法调用,那么this就是值这个对象
七、作用域和作用域链的理解
1.作用域(全局作用域、局部作用域)
1.js中首先有一个最外层的作用域,全局作用域;-
2.js中可以通过函数来创建一个独立作用域称为函数作用域,函数可以嵌套,所以作用域也可以嵌套;
3.es6中新增了块级作用域(大括号,比如:if{},for(){},while(){}…);
全局作用域:
最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的:
局部作用域:
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的
作用域链
根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问。
想要知道js怎么链式查找,就得先了解js的执行环境
八、require和import区别
两者的加载方式不同 ,规范不同
1 两者的记载方式不同,require是在运行时加载 而import是在编译时加载
require(’./a’)(); a模块是一个函数,立即执行a模块函数
var data=require(’./a’)[0] a模块导出的是一个数组
2 规范不同,require是CommonJS/AMD规范 import是ESMAScript6+规范
3 require 特点:社区方案,提供了服务器的模块加载方案。非语言层面的标准。只能在运行时确定模块的依赖关系及输入/输出的变量,无法进行静态优化
import特点:语言规范层面支持模块功能。支持编译时静态分析,便于JS引入宏和类型检验。动态绑定
九、数组方法
(1).push()向末尾添加元素(改变原数组) 返回添加后的length
(2).pop() 删除数组最后一个元素(改变原数组) 返回被删除的元素
(3).forEach()按升序依次遍历数组中的值
(4).indexOf() 查找数组中某元素的第一个索引值。[不改变原数组] 如果没有就返回-1
(5).map() 对数组中的每一个元素都进行处理,返回新的数组
(6).filter()过滤原数组,返回新数组
(7).splice() 添加/删除/替换 当前数组改变
十、跨域
==什么是跨域:
理解跨域的概念:协议、域名、端口都相同才同域,否则都是跨域
解决跨域问题:
JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
核心思想:网页通过添加一个
CORS
CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
设置document.domain解决无法读取非同源网页的 Cookie问题
因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案仅限主域相同,子域不同的跨域应用场景。)
十一、原型和原型链
- prototype
每个函数都有一个prototype属性,被称为显示原型
2._ proto _
每个实例对象都会有_ proto _属性,其被称为隐式原型
每一个实例对象的隐式原型_ proto _属性指向自身构造函数的显式原型prototype
3. constructor
每个prototype原型都有一个constructor属性,指向它关联的构造函数。
原型链
获取对象属性时,如果对象本身没有这个属性,那就会去他的原型__proto__上去找,如果还查不到,就去找原型的原型,
一直找到最顶层(Object.prototype)为止。Object.prototype对象也有__proto__属性值为null。
十二、Ajax
概念:
它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站应用的体验。
Ajax 的应用场景:
页面上拉加载更多数据
局部页面刷新
表单项离开焦点数据验证
搜索框提示文字下拉列表
Ajax 运行原理及实现
Ajax 运行原理
Ajax 相当于浏览器发送请求与接收响应的代理人,以实现在不影响用户浏览页面的情况下,局部更新页面数据,从而提高用户体验。
Ajax 的实现步骤:
1.创建 Ajax 对象
var xhr = new XMLHttpRequest();
2.Ajax 请求地址以及请求方式
xhr.open('get', 'http://www.example.com');
3.发送请求
xhr.send();
4.获取服务器端给与客户端的响应数据
xhr.onload = function () {
console.log(xhr.responseText);
}
Ajax 封装
问题:发送一次请求代码过多,发送多次请求代码冗余且重复。
解决方案:将请求代码封装到函数中,发请求时调用函数即可。
ajax({
type: 'get',
url: 'http://www.example.com',
success: function (data) {
console.log(data);
}
})
十三、.继承有哪些方式
class类继承
原理: Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多
class Animate {
constructor() {
// 默认返回实例对象 this
}
}
class Dog extends Animate {
constructor() {
super()
}
}
super 关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用
- super作为函数使用
super 作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。并且,super() 只能用在子类的构造函数之中。
class Animate {}
class Dog extends Animate {
constructor() {
super()
}
}
构造函数继承
原理:在子类中使用call调用父类方法,并将父类的this修改为子类的this,相当于把父类的实例属性复制一份放到子类里
function Person(name,age){
this.name=name
this.age=age
this.getSay=function(){
console.log("lova sea")
}
}
Person.prototype.XiHuan=function(){
console.log("ting")
}
function Man(){
Person.call(this)
this.name='tinghua'
this.age='0215'
}
Man.prototype.TiMi=function(){
console.log("tentent")
}
let p=new Man("qingdu",'1224')
console.log(p)
原型式继承
原理:让一个引用数据类型利用原型继承另一个引用数据类型的属性和方法
function Person(name ,age){
this.name=name
this.age=age
this.getSay=function(){
console.log("love sea");
}
}
Person.prototype.wenNa=function(){
console.log("ting")
}
function Children(){
this.city="BeiJing"
}
Children.prototype.guXiang=function(){
console.log("ShanDong")
}
Children.prototype=new Person("qingdu",'1224')
let p=new Children("tinghua",'0215')//子类实例不能向父类构造函数传参
console.log(p)
十四、闭包
概念:
三大特性
1.函数嵌套函数。
2.函数内部可以引用外部的参数和变量。
3.参数和变量不会被垃圾回收机制回收
闭包的优点
1、变量长期驻扎在内存中
2、避免全局变量的污染
3、私有成员的存在
闭包的缺点
1、常驻内存 增大内存的使用量
2、使用不当会造成内存的泄露
变量提升
通常JS引擎会在正式执行之前先进行一次预编译,在这个过程中,首先将变量声明及函数声明提升至当前作用域的顶端,然后进行接下来的处理
应用场景:
一、封装对象的私有属性和方法
隐藏数据,做一个简单的缓存工具
二、闭包作用回调函数
三、函数节流防抖
函数防抖是指在函数被高频触发时当停止触发后延时n秒再执行函数,(即每次触发都清理延时函数再次开始计时),一般用于resize scroll,mousemove
函数节流 原理 函数被高频出发时延时n秒后才会再次执行,防抖主要是用户触发一次时间后,延迟一段时间触发,而节流会规定的事件内触发一次事件
十五、promise
promise是一个对象 通过他可以获取异步操作的消息 也是异步编程的一种解决方案
1、主要用于异步计算
2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3、可以在对象之间传递和操作 promise,帮助我们处理队列
resolve 作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject 作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise 有三个状态:
1、pending[待定]初始状态
2、fulfilled[实现]操作成功
3、rejected[被否决]操作失败
应用场景:
ajax, axios中的get和post的封装
微信小程序封装wx.requset()
uniapp开发 uni.requsrt()
promise解决地狱回调,可以链式调用
十六、async/await
概念:
async 函数,就是 Generator 函数的语法糖,它建立在Promises上,并且与所有现有的基于Promise的
API兼容。
async和await 是一种同步的写法,但还是异步操作,async和await必须同时去写才会生效 不然的话就会报错
await可以等同步方法执行完成 在运行await后面的代码 在请求数据的时候 等待数据加载过来 才会运行await后面的代码
而且await接收的必须是个promise对象 async 和 await 主要应用是在数据接收 和解决异步问题的处理
十七、 async/await相比于Promise的优势
代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负
担
Promise传递中间值非常麻烦,而async/await几乎是同步的写法,非常优雅
错误处理友好,async/await可以用成熟的try/catch,Promise的错误捕获非常冗余
调试友好,Promise的调试很差,由于没有代码块,你不能在一个返回表达式的箭头函数中设置断
点,如果你在一个.then代码块中使用调试器的步进(step-over)功能,调试器并不会进入后续
的.then代码块,因为调试器只能跟踪同步代码的『每一步』。
十八、深拷贝和浅拷贝的区别?如何实现
1、概念:
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。浅拷贝只复制对象的第一层属性
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。对对象的属性进行递归复制
2、实现方式:
浅拷贝
使用Object.assign({},obj)第一个参数是一个空对象,第二个参数是你要复制的对象;通过这
个方法我们知道浅拷贝不能修改基础的数据类型,可以修改引用的数据类型;
ES6中的…扩展运算符来进行浅拷贝的实现;
Object.assign()实现
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目 标对象
。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
注意:
当object只有一层的时候,是深拷贝,例如如下
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100; console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
深拷贝
1、
对象只有一层的话可以使用上面的:Object.assign()函数
转成 JSON 再转回来
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20; console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到 console.log(obj2);
// { body: { a: 20 } } console.log(obj1 === obj2);
// false console.log(obj1.body === obj2.body); // false
用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
2、
使用 Object.create() 方法
直接使用 var newObj = Object.create(oldObj),可以达到深拷贝的效果。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i]; // 避免相互引用对象导致死循环,如 initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
十九、ES6新增特性(说一些常用的即可、没必要都说)
1.
新增了块级作用域(let,const)
2.
提供了定义类的语法糖(class)
3.
新增了一种基本数据类型(Symbol)
4.
新增了变量的解构赋值
5.
函数参数允许设置默认值,引入了rest参数,新增了箭头函数
6.
数组新增了一些API,如 isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法
7.
对象和数组新增了扩展运算符
8.
ES6 新增了模块化(import/export)
9.
ES6 新增了 Set 和 Map 数据结构
10.
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
11.
ES6 新增了生成器(Generator)和遍历器(Iterator)
二十、DOM操作
首先要了解什么是DOM
1、概念:
DOM全称 Document Object Model,即文档对象模型。
将文档抽象成一个树型结构,文档中的标签、标签属性或标签内容可以表示为树上的结点。
2、分类:
按照操作对象的不同,可分为Core DOM、XML DOM 和 HTML DOM。
Core Dom: 核心Dom,针对任何结构化文档的标准模型。
XML DOM: 用于XML文档的标准模型,对XML元素进行操作。
HTML DOM: 用于HTML文档的标准模型,对HTML元素进行操作。
3、功能:
① 查询某个元素
② 查询某个元素的祖先、兄弟以及后代元素
③ 获取、修改元素的属性
④ 获取、修改元素的内容
⑤ 创建、插入和删除元素
了解什么是DOM节点
1、概念:
文档中的所有内容都可表示为一个节点(node),如:HTML里整个文档、每个标签、每个标签的属性和文本都可作为一个节点。
2、节点分类:
① 文档节点(Document):整个XML、HTML文档
② 元素节点(Element):每个XML、HTML元素
③ 属性节点(Attr):每个XML、HTML元素的属性
④ 文本节点(Text):每个XML、HTML元素内的文本
⑤ 注释节点(Comment):每个注释
注意:这里的Document节点为总称,具体可分为XMLDocument和HTMLDocument,同理Element也可分为XMLElement和HTMLElement。
二十一、JavaScript中let、const、var 的区别
1.是否存在变量提升?
var声明的变量存在变量提升(将变量提升到当前作用域的顶部。
let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。
2.是否存在暂时性死区?
var不存在暂时性死区
let和const存在暂时性死区。即只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响
注:暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
3.是否允许重复声明变量?
var允许重复声明变量。
let和const在同一作用域不允许重复声明变量。
4.是否存在块级作用域?
var不存在块级作用域。
let和const存在块级作用域。
5. 是否能修改声明的变量?
var和let可以。
const声明一个只读的常量。一旦声明,常量的值就不能改变。const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
......持续更新