JavaScript1
1. 有哪些数据类型?
-
根据JavaScript中的变量类型传递方式,分为基本数据类型和引用数据类型两大类七种。
-
基本数据类型包括Undefined、Null、Boolean、Number,String,Symbol (ES6新增)六种。引用数据类型只有 Object—种,主要包括对象、数组和函数。Symbol( ) 代表独一无二的唯一性。
判断数据类型采用typeof 操作符,有两种语法:
2.基本数据类型和引用数据类型有什么区别?
两者作为函数的参数进行传递时:
-
基本数据类型传入的是数据的副本,原数据的更改不会影响传入后的数据。
-
引用数据类型传入的是数据的引用地址,原数据的更改会影响传入后的数据。
两者在内存中的存储位置∶
-
基本数据类型存储在栈中。
-
引用数据类型在栈中存储了指针,该指针指向的数据实体存储在堆中。
3.判断数据类型的方法有哪些?
-
利用typeof可以判断数据的类型。
-
A instanceof B 可以用来判断A是否为B的实例,但它不能检测null和undefined;
-
B.constructor ==A可以判断A是否为B的原型,但constructor检测object 与instanceof不一样,还可以处理基本数据类型的检测。
-
Object.prototype.toString.call()。Object.prototype.toString.call()是最准确最常用的方式。
4.浅拷贝与深拷贝有何区别?如何实现?
-
浅拷贝:创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
实现方法:
-
Object.assign( )方法
let obj1 = {
name: "小李",
age: 18
}
// Object.assign() 会返回一个新对象
// 这种写法和下面写法实现效果一样
// let obj2 = Object.assign({}, obj1)
let obj2 = {}
Object.assign(obj2, obj1)
obj1.name = '小张';
obj1.age = '25';
console.log(obj1); // {name:"小张", age:25}
console.log(obj2); // {name:"小李", age:18} -
concat()方法
let arr = [1,2,3,4];
let newArr = arr.concat()
arr.push(5);
console.log(arr); // [1,2,3,4,5]
console.log(newArr ); // [1,2,3,4]
-
扩展运算符
let obj1 = {
name: "小李",
age: 18
}
let obj2 = {...obj1}
obj1.name = '小张';
obj1.age = '25';
console.log(obj1, obj2);
let arr = [1, 2, 3]
let newArr = [...arr]
arr[0] = 999
console.log(arr, newArr)
-
深拷贝
-
利用json数据和json字符串之间的转换。JSON.stringify(),JSON.parse()。
-
$.extend() $.extend(true, {}, obj1)。
-
递归。
function deepClone(obj) {
// 定义一个对象,用来确定当前的参数是数组还是对象
let objClone = Array.isArray(obj) ? [] : {};
// 判断obj是否存在,且类型是对象。(typeof [] 也是 object)
if (obj && typeof obj === "object") {
// 遍历参数的键
for (key in obj) {
// hasOwnProperty() 方法不会检测对象的原型链,只会检测当前对象本身存在该属性时才返回 true,常与for...in使用
// 判断对象是否存在该属性
if (obj.hasOwnProperty(key)) {
// 值是对象就递归
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
// 基本数据类型 直接赋值
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
let a = {
name: "小李",
age: 18,
father: {
name: "大李",
},
fn: function () {
return 123
}
}
let b = deepClone(a)
a.father.name = "小李他爸"
console.log(a, b);
5.什么是执行上下文和执行栈?
-
变量或函数的执行上下文,决定了它们的行为以及可以访问哪些数据。每个上下文都有一个关联的变量对象,而这个上下文中定义的所有变量和函数都存在于这个对象上(如DOM中全局上下文关联的便是window对象)。
-
每个函数调用都有自己的上下文。当代码执行流进入函数时,函数的上下文被推到一个执行栈中。在函数执行完之后,执行栈会弹出该函数上下文,在其上的所有变量和函数都会被销毁,并将控制权返还给之前的执行上不文。JS的执行流就是通过这个执行栈进行控制的。
6.什么是作用域和作用域链?
-
作用域可以理解为一个独立的地盘,可以理解为标识符所能生效的范围。作用域最大的用处就是隔离变量;不同作用域下同名变量不会有冲突。ES6中有全局作用域、函数作用域和块级作用域三层概念。
-
当一个变量在当前块级作用域中未被定义时,会向父级作用域(创建该函数的那个父级作用域)寻找。如果父级仍未找到,就会再一层一层向上寻找,直到找到全局作用域为止。这种一层一层的关系,就是作用域链。
7.作用域和执行上下文的区别是什么?
-
函数的执行上下文只在函数被调用时生成,而其作用域在创建时已经生成。
-
函数的作用域会包含若千个执行上下文(有可能是零个,当函数未被调用时。
8.this指向的各种情况都有什么?
-
全局作用域中的函数:其内部this指向window。
var a = 1;function fn(){console.log(this.a)}; fn()
-
对象内部的函数:其内部this指向对象本身.
var a = 1;
var obj = {
a:2,
fn:function(){
console.log(this.a)}
}
}
obj.fn()//输出2
-
构造函数:其内部this 指向生成的实例:
function createP(name,age){
this.name = name //this.name指向P
this.age = age //this.age指向P
}
var p = new createP("老李",46)
-
箭头函数:箭头函数没有自己的this,看其外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是 window。
9.如何改变this指针的指向?
-
call方法:fn1.call();
-
call()方法可以进行普通函数的调用。
-
call()方法可以改变this的指向,如果没有参数,this指向window。
-
call()方法可以改变this的指向,如果有一个参数,this指向该参数。
-
call()方法可以改变this的指向,如果有多个参数,this指向第一个参数,剩下的是个参数列表(构造函数继承的案例)。
-
-
apply方法:fn1.apply()
-
apply()方法可以进行普通函数的调用
-
apply()方法可以改变this的指向,如果没有参数,this指向window
-
apply()方法可以改变this的指向,如果有一个参数,this指向该参数
-
apply()方法可以改变this的指向,如果有多个参数,第一个参数是null或者window,第二个参数是数组。
-
-
bind方法:
-
bind()不能进行函数的调用。
-
可以改变this指向。
-
10.什么是闭包?
-
闭包就是引用了其他函数作用域中变量的函数,这种模式通常在函数嵌套结构中实现。里面的函数可以访问外面函数的变量,外面的变量的是这个内部函数的一部分。闭包有如下作用。
-
闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包使用完了后,要立即释放资源,将引用变量指向null。
11.什么是原型、原型链?
-
原型:JS声明构造函数(用来实例化对象的函数)时,会在内存中创建一个对应的对象,这个对象就是原函数的原型。构造函数默认有一个 prototype 属性,prototype的值指向函数的原型。同时原型中也有一个constructor属性,constructor的值指向原函数。
-
通过构造函数实例化出来的对象,并不具有prototype属性,其默认有一个proto__属性,proto__的值指向构造函数的原型对象。在原型对象上添加或修改的属性,在所有实例化出的对象上都可共享。
12.何为防抖和节流?如何实现?
-
防抖和节流都是防止短时间内高频触发事件的方案。
-
防抖的原理是∶如果―定时间内多次执行了某事件,则只执行其中的最后一次。
-
节流的原理是:要执行的事件每隔―段时间会被冷却,无法执行。
-
应用场景有︰搜索框实时搜索,滚动改变相关的事件。
13.如何理解同步和异步?
-
同步:按照代码书写顺序——执行处理指令的一种模式,上一段代码执行完才能,执行下—段代码。
-
异步:可以理解为一种并行处理的方式,不必等待一个程序执行完,可以执行其它的任务。
-
JS之所以需要异步的原因在于JS是单线程运行的。常用的异步场景有:定时器、ajax请求、事件绑定。
14.JS是如何实现异步的?
JS引擎是单线程的。但又能实现异步的原因在于事件循环和任务队列体系。
事件循环:
-
JS会创建一个类似于,while (true)的循环,每执行―次循环体的过程称之为Tick。每次Tick .的过程就是查看是否有待处理事件,如果有则取出相关事件及回调函数放入执行栈中由主线程执行。待处理的事件会存储在—个任务队列中,也就是每次Tick会查看任务队列中是否有需要执行的任务。
15.什么是 AJAX?如何实现?
ajax是一种能够实现局部网页刷新的技术,可以使网页异步刷新。ajax的实现主要包括四个步骤。
-
创建核心对象XMLhttpRequest ;
-
利用open方法打开与服务器的连接;
-
利用send方法发送请求;(POST"请求时,还需额外设置请求头)
-
监听服务器响应,接收返回值。
//创建XMLHTTPRequest对象
const xhr = new XMLHttpRequest();
//创建HTTP请求
//1、不带参数的get请求
//xml.open("get","url路径");
//2、带参数的get请求
xml.open("get", "url路径?key1=value1&key2=value2");
xhr.send();
//监听事件
xhr.onreadystatechange = function () {
//判断请求状态码readyState成功为4,状态码status成功为200
if (xhr.readyState === 4 && xhr.status === 200)
//得到服务端返回的文本数据
//此处输出的是JSON字符串类型,转为普通类型,方便之后的DOM操作
console.log(JsoN.parse(xhr.responseText));
}
16.实现异步的方式有哪些?
-
回调函数模式:将需要异步执行的函数作为回调函数执行,其缺点在于处理复杂逻辑异步逻辑时,会造成回调地狱(回调嵌套层数太多,代码结构混乱);
-
事件监听模式:采用事件驱动的思想,当某—事件发生时触发执行异步函数其缺点在于整个代码全部得变为事件驱动模式,难以分辨主流程;
-
发布订阅模式:当异步任务执行完成时发布消息给信号中心,其他任务通过在信号中心中订阅消息来确定自己是否开始执行;
-
Promise(ES6): Promise对象共有三种状态 pending(初始化状态)、fulfilled(成功状态)、rejected(失败状态)。
-
async/await(ES7):基于Promise实现的异步函数。
-
利用生成器实现。
17.怎么理解 Promise对象?
Promise对象有如下两个特点:
-
对象的状态不受外界影响。Promise对象共有三种状态pending. fulfilled.rejected。状态值只会被异步结果决定,其他任何操作无法改变。
-
状态一旦成型,就不会再变,且任何时候都可得到这个结果。状态值会由pending变为fulfilled 或rejected,这时即为resolved。
Promise 的缺点有如下三个缺点:
-
Promise一旦执行便无法被取消;
-
不可设置回调函数,其内部发生的错误无法捕获;
-
当处于pending状态时,无法得知其具体发展到了哪个阶段。
Pomise中常用的方法有:
-
Promise.prototype.then(): Promise 实例的状态发生改变时,会调用then内部的回调函数。then方法接受两个参数(第一个为resolved状态时时执行的回调,第一个为rejected状态时时执行的回调)
-
Promise.prototype.catch():.then(null, rejection)或.then(undefined,rejection)的别名,用于指定发生错误时的回调函数。
18.怎么理解宏任务,微任务?? ?
-
宏任务有:script(整体代码)、 setTimeout、setlnterval、l/O、页面渲染。
-
微任务有:Promise.then.Object.observe。
-
执行顺序大致如下:主线程任务——>宏任务——>微任务—一>微任务里的宏任务。
19.什么是跨域?怎么解决跨域问题?
-
跨域问题实际是由同源策略衍生出的一个问题,当传输协议、域名、端口任一部分不―致时,便会产生跨域问题,从而拒绝请求,但 <linkhref=xXXs <script src=XXX>;天然允许跨域加载资源。解决方案有:
-
JSONP:(1)原理;利用<script>;标签没有跨域限制的漏洞,使得网页可以得到从其他来源动态产生的JSON数据(前提是服务器支持)。(2)优点:实现简单,兼容性好。(3)缺点;仅支持get方法,容易受到XSS攻击。
-
CORS:(1)原理:服务器端设置Access-Control-Allow-Origin 以开启CORS。该属性表示哪些域名可以访问资源,如设置通配符则表示所有网站均可访问。
-
nginx反向代理:(1)类似Node中间件服务器,通过nginx 代理服务器实现。(2)实现方法:下载安装nginx,修改配置。
20.实现继承的方法有哪些?? ?
-
原型链继承:
function Cat(){};
Cat.prototype = new Animal();
Cat.prototype.name = 'cat ' ; -
class+extends 继承(ES6)
class Animal {
constructor(name) {
this.name = name
}
}
-
借用构造函数继承
function Animal(name){
this.name = name
}
function Cat0{
Animal.call(this,"CatName")//重点,调用父类的call方法
}
21.DOM事件模型和事件流?
DOM事件模型包括事件捕获(自上而下触发)与事件冒泡(自下而上触发, ie 用的就是冒泡)机制。基于事件冒泡机制可以完成事件代理。
22.require/import之间的区别?
-
require 是 CommonJS语法,import是ES6语法;
-
require只在后端服务器支持,import在高版本浏览器及Node 中都可以支持;
-
require引入的是原始导出值的复制,import则是导出值的引用;
-
require时运行时动态加载;import是静态编译;
-
require调用时默认不是严格模式,import 则默认调用严格模式.