JavaScript知识点整理

2019-02-18  本文已影响0人  沫之

JavaScript是一门什么样的语言?有哪些特点?

运行在浏览器的V8引擎中的脚本语言,不要编译就可以由解释器直接运行的,此外变量松散定义,属于弱类型语言。

说几条JavaScript的基本规范?

页面渲染原理

预编译

defer和async

数据类型

原始类型有哪几种?null是对象吗?

原始类型有:boolean,string,number,null,undefined,symbol 6种。

null不是对象,typeof null会输出object,是因为以前32为系统会将000开头的代表为对象,但null表示为全零所以被错误判断成object

原始类型和引用类型的区别

1.原始类型存储的是值,引用类型存储的是指针。 2.原始数据类型直接存储在栈中,引用数据类型存储在堆中。

使用typeof可以得到哪些类型?

undefined,string,number,boolean,object,function

typeof只能区分除null的原始类型,引用类型只能区分function。

什么是提升?什么是暂时性死区?var,let及const区别

原始类型

为什么0.1+0.2!=0.3?如何解决这个问题?

在计算机中,数字以二进制形式存储。在JavaScript中数字采用IEEE754的双精度标准进行存储,因为存储空间有限,当出现无法整除的小数时会取一个近似值。

在0.1+0.2中0.1和0.2都是近似表示的,所以相加时两个近似值进行计算导致最后结果为0.3000,0000,0000,0004,此时对于JS来说不够近似于0.3,所以出现了0.1+0.2!=0.3

解决方法:parentFloat((0.1+0.2).toFixed(10)) === 0.3 // true

null和undefined的区别

undefined表示变量被声明了但还没有被赋值

null表示没有此变量,是空的

Symbol的使用场景

作为属性名的使用

var mySymbol=Symbol();
var a={};
a[mySymbol]='hello'

操作符

何时使用===,何时使用==

==会进行类型转换后再比较,===不会,尽量都用===.

以下两种情况可以用==

var obj={}
if(obj.a == null){
    // 相当于obj.a===null || obj.a===undefined
}
function fn(a, b){
    if(b == null){
        // 相当于b===null || b===undefined
    }
}

Object.is()与===,==的区别?

Object.is()可以说是升级版,Object.is(NaN,NaN)会返回true,Object.is(-0,+0)返回false

引用类型

Array类型

如何判断一个变量是数组?

1.判断是否具有数组某些方法

if(arr.slice()){}

2.instanceof(某些IE版本不正确)

arr instanceof Array

3.Array.isArray()

4.Object.prototype.toString.call(arr); // '[object Array]'

5.constructor方法

arr.constructor === Array

数组的方法

// 会改变原数组
pop()       // 末尾删除
push()      // 末尾新增
shift()     // 开头删除
unshift()   // 开头新增
reverse()   // 数组反转
sort()      // 排序
splice()    // 修改数组(删除插入替换)
// 不会改变原数组
concat()    // 合并数组
slice()     // 选择数组的一部分
indexOf()   // 顺序查找指定元素下标
lastIndexOf()   // 倒序查找指定元素下标
// 迭代方法
// every()查询数组是否每一项都满足条件
// some()查询数组中是否有满足条件的项
// filter()过滤,返回true的项组成的数组
// map()对每一项运行给定函数,返回每次函数调用结果组成的数组
// forEach()对每一项运行给定函数,无返回值
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.every(function(item,index,array){
    return item>2;
})  // false
numbers.some(function(item,index,array){
    return item>2;
})  // true
numbers.filter(function(item,index,array){
    return item>2;
})  // [3,4,5,4,3]
numbers.map(function(item,index,array){
    return item*2;
})  // [2,4,6,8,10,8,6,4,2]
numbers.forEach(function(item,index,array){
    // 执行某些操作
})  // 无返回值
// 归并方法
// reduce()从第一项开始逐个遍历到最后
// reduceRight()从最后一项开始向前遍历到第一项
var values = [1,2,3,4,5];
values.reduce(function(prev,cur,index,array){
    return prev+cur;
}) // 15
// reduceRight()结果一样,顺序相反

原生sort使用的是哪些排序算法?

插入排序和快速排序结合的排序算法

['1','2','3'].map(parseInt)的答案是?

[1,NaN,NaN]

因为parentInt需要两个参数(val,radix),radix表示解析进制,而map传入三个(element,index,array)导致对应的radix不合法导致解析错误。

split()和join()的区别

join()将数组中的元素放入一个字符串中,split将字符串分割成数组

var arr=[1,2,3];
var str=arr.join('|'); // '1|2|3'
str.split('|'); // [1,2,3]

Date()类型

获取2019-02-16格式的日期

function formatDate(dt){
    if(!dt){
        dt=new Date()
    }
    var year = dt.getFullYear();
    var month = dt.getMonth()+1;
    var day = dt.getDate();
    if(month<10){
        month= '0'+month;
    }
    if(day<0){
        day = '0'+day;
    }
    var formatDt = year+'-'+month+'-'+day
    return formatDt;
}

其他一些方法

getHour()       // 返回时
getMinutes()    // 返回分
getSeconds()    // 返回秒
getDay()        // 返回星期天数
getTime()       // 返回毫秒数

Function类型

函数声明和函数表达式

解析器会先读取函数声明,提升到最前。而函数表达式会等到执行到它所在的代码才真正被解释执行

// 函数声明
function sum(a,b){
    return a+b;
}
// 函数表达式
var sum = function(a,b){
    return a+b;
}

谈谈对this对象的理解

this引用函数执行的环境对象,总是指向函数的直接调用者,在执行时才能确定值

this的指向

1.默认绑定,在浏览器中为window,在node中是global

2.隐式绑定 例:window.a()

3.显式绑定

4.new绑定

bind,call和apply各自有什么区别?

相同点

callee和caller的作用?

callee是arguments对象的一个属性,指向arguments对象的函数即当前函数。递归可以使用arguments.callee()。 箭头函数中this作用域与函数外一致,且没有arguments对象,所以箭头函数没有callee

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*arguments.callee(num-1)
    }
}

caller是函数对象的一个属性,指向调用当前函数的函数,比如A调用B,则B.caller指向A()。全局作用域下调用当前函数,caller的值为null

基本包装类型

Number

toFixed()按指定小数位返回数值的字符串表示

var num=10; num.toFixed(2); // '10.00'

String

// charAt()根据字符位置返回所在位置的字符串
// charCodeAt()根据字符位置返回所在位置字符串的字符编码
var str = 'hello world';
str.charAt(1);      // 'e'
str.charCode(1);    // '101'

// fromCharCode() 接收字符编码转为字符串
String.fromCharCode(104,101,108,108,111) //'hello'

// concat()将字符拼接起来得到新字符串
var str="hello"
str.concat(' world'); // "hello world"

// indexOf()和lastIndexOf() 返回字符位置
// trim() 删除空格
// toLowerCase() 转小写,toUpperCase() 转大写
// localeCompare()  根据字母表比较排序

slice,substr和substring的区别

slice和substring接收的是起始位置和结束位置,substr接收的是起始位置和所要返回的字符串长度

对于负数,slice会将负数与字符串长度相加,substr会将负的第一个参数加上字符串长度,第二个参数转为0,substring会将所有负数转为0

单体内置对象

Global对象

URI编码方法

encodeURI和encodeURICcomponent encodeURI和decodeURICcomponent eval的作用? 功能是将对于字符串解析出JS代码执行。要避免使用eval,不安全且非常消耗性能

Math对象

var max=Math.max(3,54,32,16); // 54

原型和原型链

原型

构造函数

构造函数特点:函数名首字母大写,它就类似一个模板,可以new出多个实例

var a={} // var a=new Object()的语法糖
var a=[] // var a=new Array()的语法糖
function Foo(){} // var Foo=new Function()的语法糖

instanceof

instanceof判断引用类型属于哪个构造函数

f instanceof Foo的判断逻辑:f的proto一层层往上,能否找到Foo.prototype。

但是因为原型链上所有特殊对象的proto最终都会指向Object.prototype,所以instanceof判断类型也不完全准确

new操作符具体干了什么?

function create(){
    let obj = {}
    let Con = [].shift.call(arguments)
    obj.__protu__ = Con.prototype
    let result = Con
}

通过new的方式创建对象和通过字面量创建的区别

更推荐字面量的方式创建对象,性能和可读性都更好。使用var o=new Object()和var o={}的区别是前者会调用构造函数

hasOwnProperty

hasOwnProperty判断该对象本身是否有指定属性,不会到原型链上查找。

使用方法:object.hasOwnProperty(proName)

利用它可以循环对象自身属性

for(let item in f){
    if(f.hasOwnProperty(item)){
        console.log(item)
    }
}

原型链

访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着proto这条链向上找,这就是原型链

面向对象

创建对象的几种方式

function createPerson(name, age){
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name)
    }
    return o;
}
var person1 = createPerson('chen',21)

没有显示创建对象,直接将属性方法赋给this,没有return语句

function Person(name, age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name)
    }
}
var person1 = new Person('chen',21)

缺点:每个方法都要在每个实例上重新定义一遍,无法得到复用

function Person(){}
Person.prototype.name="chen"
Person.prototype.age=21
Person.prototype.sayName=function(){
    console.log(this.name)
}
var person1 = new Person()

缺点:所有实例都取得相同属性值

function Person(name,age){
    this.name=name;
    this.age=age;
}
Person.prototype = {
    constructor: Person,
    sayName: function(){
        console.log(this.name)
    }
}
var person1=new Person('chen',21)

实现继承

JavaScript通过原型链实现继承

function Parent(){
    this.name = 'parent'
}
Parent.prototype.sayName = function(){
    return this.name
}
function Child(){
}
// 继承了Parent
Child.prototype = new Parent();
var child1=new Child();
child1.say();

缺点:对象实例共享所有继承的属性和方法

function Parent(){
    this.arr=[1,2,3]
}
function Child(){
    // 继承了Parent
    Parent.call(this)
}
var child1 = new Child();
child.arr.push(4); // [1,2,3,4]
var child2 = new Child();
child.arr;  // [1,2,3]

使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性

function Parent(name){
    this.name = name;
    this.arr = [1,2,3]
}
Parent.prototype.sayName = function(){
    console.log(this.name)
}
function Child(name,age){
    // 继承属性
    Parent.call(this, name)
    this.age=age
}
// 继承方法
Child.prototype = new Parent()
Child.prototype.constructor = Child;
Child.prototype.sayAge = function(){
    console.log(this.age)
}
var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4]
child1.sayName()    // 'chen'
child1.sayAge()     // 21

var child2=new Child('miao', 12)
child2.arr          // [1,2,3]
child2.sayName()    // "miao"
child2.sayAge()     // 12

缺点:无论在什么情况都会调用两次父构造函数,一次是创建子类型原型,另一次是在子构造函数内部

var person = {
    name: 'chen',
    arr: [1,2,3]
}
var person1 = Object.create(person);
person1.name = 'run'
person1.arr.push(4)
var person2 = Object.create(person);
person2.name = 'miao'
person2.arr.push(5)
person.arr; // [1,2,3,4,5]
function create(original){
    // 通过调用函数创建一个新对象
    var clone = object(original); 
    // 以某种方式增强对象
    clone.sayHi = function(){
        console.log('hi')
    }
    return clone;
}
var person = {
    name: 'chen'
}
var person1 = create(person);
person1.sayHi();
function Parent(name){
    this.name = name;
    this.arr = [1,2,3]
}
Parent.prototype.sayName = function(){
    console.log(this.name)
}
function Child(name,age){
    // 继承属性
    Parent.call(this, name)
    this.age=age
}
// 继承方法
var prototype = Object(Parent, Child);
prototype.constructor = Child;
Child.prototype = prototype;

var child1=new Child('chen',21);
child1.arr.push(4); //[1,2,3,4]
child1.sayName()    // 'chen'
child1.sayAge()     // 21

var child2=new Child('miao', 12)
child2.arr          // [1,2,3]
child2.sayName()    // "miao"
child2.sayAge()     // 12

class如何实现继承,class本质是什么?

class只是语法糖,本质是函数

class Parent{
    constructor(value){
        this.val=value
    }
    getValue(){
        console.log(this.val)
    }
}
class Child extends Parent{
    constructor(value){
        super(value)
        this.val = value
    }
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true

核心:使用extends表明继承自哪个父类,并且在子类构造函数中必须使用super,可以看做是Parent.call(this,value)

什么是面向对象编程及面向过程编程,它们的异同和优缺点

面向过程就是分析出解决问题所需的步骤,然后用函数把这些步骤一步步实现,使用的时候一个个依次调用

面向对象是把构成问题的事务分解成各个对象,奖励对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。

优点:易维护,可读性高,易扩展,继承性高,降低重复工作量,缩短了开发走起

闭包

闭包指有权访问另一个函数内部变量的函数,当在函数内部定义了其他函数,也就创建了闭包

谈谈你对闭包的理解

使用闭包可以模仿块级作用域

优点:可以避免全局变量的污染,实现封装和缓存;

缺点:闭包会常驻内存,增大内存使用量,使用不当很容易造成内存泄漏。解决方法:在退出函数之前,将不适用的局部变量设为null。

闭包最大的两个用处:1.可以读取函数内部的变量;2.使这些变量始终保存在内存中;3.封装对象的私有属性和私有方法

说说你对作用域链的理解

作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能享受访问,访问到window对象即被终止。

简单来说,作用域就是变量和函数的可访问范围

如何创建块级作用域

BOM

BOM对象有哪些?

window对象的方法

alert(),prompt(),confirm(),open(),close(),print(),focus(),blur(),moveBy(),moveTo(),resizeBy(),resizeTo(),scrollBy(),scrollTo(),setInterval(),setTimeout(),clearInterval(),clearTimeout()

history对象

history.go(-1);     // 后退一页
history.go(1);      // 前进一页
history.back();     // 后退一页
history.forward();  // 前进一页

窗口位置

var leftPos=(typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX;
var topPos=(typeof window.screenTop == 'number') ? window.screenTop : window.screenY;

moveTo():接收新位置的x,y坐标值

moveBy():接收在水平垂直方向上移动的像素数

窗口大小

outerWidth和outerHeight返回浏览器窗口本身的尺寸,innerWidth和innerHeight返回容器视图区的大小

如何检测浏览器的类型

使用navigator.userAgent

var ua = navigator.userAgent;
var isChrome = ua.indexOf('Chrome')

拆解url的各部分

使用location的属性

history.go(1)

DOM

为什么操作DOM慢?

因为DOM属于渲染引擎的东西,JS又是JS引擎的东西,当我们通过JS操作DOM的时候,涉及到两个线程间的通信,而且操作DOM可能会带来重绘回流的情况,所以就导致了性能问题。

插入几万个DOM,如何实现页面不卡顿?

什么情况阻塞渲染

解决方法

重绘Repaint和回流Reflow

重绘是当节点改变样式而不影响布局,回流是当布局或几何属性需要改变

回流必定会发生重绘,回流的成本比重绘高

性能问题:1.改变window大小 2.改变字体 3.添加或删除样式 4.文字改变 5.定位或浮动 6.盒模型

减少重绘和回流

创建节点

注意事项:

页面修改API

节点查询API

节点关系型API

元素属性API

元素样式API

ele.style.color = 'red'
ele.style.setProperty('font-size', '16px')
ele.style.removeProperty('color')
var style = document.createElement('style');
style.innerHTML='body{color:red;} #top{color:white;}';
document.head.appendChild(style);

attribute和property的区别是什么?

attribute是dom元素在文档中作为HTML标签拥有的属性

prototype是dom元素在JS中作为对象拥有的属性

JS如何设置获取盒模型对于的宽和高?

offsetWidth/offsetHeight,clientWidth/clientHeight与srcollWidth/scrollHeight的区别

document.write和innerHTML的区别

DOM事件

DOM事件的级别

DOM0级事件就是将一个函数赋值给一个事件处理属性,缺点在于一个处理程序无法同时绑定多个处理函数。

DOM2级事件运行给一个程序添加多个处理函数,定义了addEventListener和removeEventListener两个方法,分别用于绑定和解绑事件,方法包含三个参数分别是绑定的事件处理的属性名称,处理函数,是否在捕获时执行事件

IE8以下使用attachEvent和detachEvent实现,不需要传入第三个参数,因为IE8以下只支持冒泡型事件

btn.attachEvent('onclick', showFn);
btn.detachEvent('onclick', showFn);

DOM3级事件是在DOM2级事件的基础上添加很多事件类型如load,scroll,blur,focus,dbclick,mouseup,mousewheel,textInput,keydown,keypress,同时也允许使用者自定义一些事件。

如何使用事件?

<div onclick="clicked()"></div>

优缺点:简单,但与HTML代码紧密耦合,更改不方便

document.onclick = function(){}; // 指定事件
docuemtn.onclick = null;         // 删除事件

优缺点:简单且跨浏览器

addEventListener('click', function(){},布尔值) // 指定事件
removeListener('click', function(){}, 布尔值) // 移除事件

优缺点:可以添加多个监听函数,如果指定处理函数是匿名函数,则无法删除

attachEvent('onclick', function(){}) // 指定事件
detachEvent('click', function(){}) // 移除事件

优缺点:可以添加多个监听函数,如果指定处理函数是匿名函数,则无法删除

IE与标准DOM事件模型之间存在的差别

attachEvent()第一个参数比addEventListener()的事件名多一个“on”;且没有第三个参数,因为IE事件模型只支持冒泡事件流

IE中事件处理程序处于全局作用域,其内的this会指向window,而DOM事件模型是作用于元素,其内的this执行所属元素

阻止冒泡

IE:cancelBubble=true

DOM: stopPropagation()

阻止元素默认事件

IE:returnValue=false

DOM:preventDefault()

事件目标

IE: srcElement DOM: target

IE与标准有哪些兼容性写法?

var ev = ev || window.event;
document.docuemntElement.clientWidth || document.body.clientWidth
var target = ev.srcElement || ev.target

DOM事件模型(冒泡和捕获)

addEventListener第三个参数为false,事件在冒泡时候执行,事件目标target一级一级往上冒泡

如何阻止事件冒泡

child.addEventListener('click', function(e){
    console.log('目标事件')
    e.stopPropagation();
}, false)

事件捕获是自上而下执行的,将addEventListener第三个参数为true

DOM事件流

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段,处于目标阶段和事件冒泡阶段。首先发送的是事件捕获,为截获事件提供了机会,然后是实际的目标接收到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应

捕获DOM事件捕获的具体流程

window->docuemnt->html->body->...

一开始接收事件的window,window接收完以后给到document,第三个才是html标签,再就是body,然后在一级一级往下传。与之相当的就是冒泡,从当前元素到window的过程

Event对象的常见应用

自定义事件

var event = new Event('custome');
ev.addEventListener('custome', function(){
    console.log('custome');
});
ev.dispatchEvent(event);

mouseover和mouseenter的区别

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout

mouseenter: 当鼠标移入元素本身(不包含子元素)会触发事件,不会冒泡,对应的移除事件是mouseleave

请解释什么是事件代理?

事件代理(事件委托)把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。原理是DOM元素的事件冒泡。

好处:可以提高性能,大量节省内存占用,减少事件注册,可以实现新增子对象时无需再次对其绑定

document.load和document.ready的区别

document.onload是在样式结构加载完才执行,window.onload()不仅要样式结构加载完还要执行完所有样式图片资源文件全部加载完后才会触发

document.ready原生中无此方法,jQuery中有$().ready(),ready事件不要求页面全加载完,只需要加载完DOM结构即可触发。

JSON

如何理解JSON?

JSON是JS的一个内置对象,也是一种轻量级的数据交换格式。 常用两个方法:

XML和JSON的区别?

Ajax

如何创建Ajax?

// 创建XMLHTTPRequest对象
var xhr = new XMLHttpRequest();
// 创建一个新的http请求
xhr.open("get", url, true)
// 设置响应HTTP请求状态变化的函数
xhr.onreadystatechange = function(){
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            // 获取异步调用返回的数据
            alert(xhr.responseText)
        }
    }
}
// 发送HTTP请求
xhr.send(null);

状态码readyState说明:0:未初始化,未调用send();1:已调用send(),正在发生请求;2:send()方法执行完毕,已经接收到全部响应内容;3:正在解析响应内容;4:解析完成,可以在客户端调用了

Ajax有哪些优缺点?

优点:1.通过异步模式,提升用户体验;2.优化了浏览器和服务器之间的传输,减少不必要的数据往返,减少了带宽占用;3.Ajax在客户端运行,承担了一部分本来由服务器承担的工作,减少了大用户量下的服务器负载;4.Ajax可以实现局部刷新

缺点:1.Ajax暴露了与服务器交互的的细节;2.对搜索引擎的支持较弱;3.不容易调试

GET和POST在浏览器实现的区别

GET请求传参的误区

误区:我们常认为GET请求参数的大小存在限制,而POST无限制

实际上HTTP协议没有限制GET/POST的请求长度限制,GET的最大长度限制是因为浏览器和WEB服务器限制了URL的长度。

GET和POST的区别

GET和POST方法没有实质区别,只是报文格式不同。

跨域

可以跨域的三个标签<img><link><script>

什么是跨域?

跨域指通过JS在不同的域之间进行数据传入或通信。 协议,域名,端口有一个不同就是不同域

浏览器为什么要使用同源策略?

同源策略是为了防止CSRF攻击,它是利用用户的登录态发起恶意请求。如果没有同源策略,网站可以被任意来源的Ajax访问到内容。

如何解决跨域问题?

function jsonp(url, callback, success){
    let script = docuemnt.createElement('scipt');
    script.src = url;
    script.async = true;
    script.type = "text/javascript";
    window[callback] = function(data){
        success && success(data)
    }
    document.body.appendChild(script)
}
jsonp('http://xxx.com', 'callback', function(value){
    console.log(value);  
})

JSONP使用简单,兼容性不错但仅限GET请求

// 发送消息
window.parent.postMessage('message', 'http://www.test.com')
// 接收消息
let mc=new MessageChannel()
mc.addEventListener('message', event => {
    let origin = event.origin || event.originalEvent.origin;
    if(origin === 'http://www.test.com'){
        console.log('验证通过')
    }
})

存储

请描述cookie,sessionStorage和localStorage的区别

答:

有几种方式可以实现存储功能?

cookie,sessionStorage,localStorage,indexDB。

indexDB不限存储大小,不与服务器端通信,除非主动删除否则一直存在。

什么是Service Worker?

Service Worker是运行在浏览器背后的独立线程,一般可以用来实现缓存功能,传输协议必须为https

Server Worker的工作?

ES6

箭头函数

箭头函数与普通函数的区别:

扩展运算符

let nodeList = document.querySelectorAll('div')
let arr=[...nodeList]
let arr1=[1,2,3]
let arr2=[4,5,6]
let arr3=[...arr1, ...arr2]

for...of循环

for-of与for-in的区别

for...of的原理

利用了遍历对象内部的iterator接口,将for...of循环分解为最原始的for循环

Promise

Proxy

Vue3.0其中一个核心功能就是使用Proxy替代Object.defineProperty Proxy可以在目标对象前架设一个拦截器,一般Reflect搭配,前者拦截对象,后者返回拦截的结果

Object.defineProperty的不足

1.无法探测到对象根属性的添加和删除
2.无法探测到对数组基于下标的修改
3.无法探测到.length修改的监测

原文:https://juejin.im/post/5c67773a5188256284529d8c

上一篇 下一篇

猜你喜欢

热点阅读