05-JavaScript的数据类型有哪些?
基本数据类型:number、string、boolean、null、undefined引用数据类型:function、object、Array
06-什么是显示转换隐式转换?
强制转化成字符串 toString() String()转换成数字 Number()、 parseInt()、 parseFloat()转换成布尔类型 Boolean()隐式拼接字符串例子 var str = "" + 18- / % === ==结论:1.有字符串就字符串拼接,字符串拼接的规范大于科学计算2.null参与科学计算的时候值为0,true=1,false=03.undefined与任何不为字符串的值相加都是 NaN ,如果计算不出来就找NaN背锅
07-什么是JS的DOM事件流?
1、事件捕获阶段:事件传播由目标节点的祖先节点逐级传播到目标节点。先由文档的根节点 document(window)开始触发对象,最后传播到目标节点,从外向内捕获事件对象2、处于目标阶段:事件到达目标对象,事件触发,如果事件不允许冒泡,事件会在这一 阶段停止传播3、事件冒泡阶段:从目标节点逐级传播到 document 节点
08-什么是事件委托?优点缺点有哪些?
JavaScript 事件代理则是一种简单的技巧,通过它你可以把事件处理器添加到一个上级元 素上,这样就避免了把事件处理器添加到多个子级元素上。当我们需要对很多元素添加事 件 的时候,可以通过将事件添加到它们的上级元素而将事件委托给上级元素来触发处理函 数。这主要得益于浏览器的事件冒泡机制。事件代理用到了两个在 JavaSciprt 事件中常被 忽 略的特性:事件冒泡以及目标元素。优点:1、减少事件注册,节省内存。比如,2、在 table 上代理所有 td 的 click 事件。3、在 ul 上代理所有 li 的 click 事件。4、简化了 dom 节点更新时,相应事件的更新。比如5、不用在新添加的 li 上绑定 click 事件。6、当删除某个 li 时,不用移解绑上面的 click 事件。缺点:1、事件委托基于冒泡,对于不冒泡的事件不支持2、层级过多,冒泡过程中,可能会被某层阻止掉。3、理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托, 比如在 table 上代理 td,而不是在 document 上代理 td。4、把所有事件都用代理就可能会出现事件误判。比如,在 document 中代理了所有 button 的 click 事件,另外的人在引用改 js 时,可能不知道,造成单击 button 触发了两个 click 事件
09-什么是深拷贝,浅拷贝
浅拷贝:拷贝栈上的数据(直接赋值)
深拷贝:拷贝堆上的数据(赋值前对数据类型做判断)
var obj = {
id: "1",
name: "andy",
msg: {
age: 18
},
color: ['pink', 'red']
}
var o = {};
function deepCopy(newObj, oldObj) {
for (var k in oldObj) {
// 判断我们的属性属于那种数据类型
// 1. 获取属性值 oldObj[k]
var item = oldObj[k];
// 2. 判断这个值是否是数组
if (item instanceof Array) {
newObj[k] = [];
deepCopy(newObj[k], item);
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newObj[k] = {};
deepCopy(newObj[k], item);
} else {
// 4. 属于简单数据类型
newObj[k] = item;
}
}
}
deepCopy(o, obj);
10-数组常用方法
push()尾插
pop()尾删
shift()头删
unshift()头插
splice()任意位置添加、删除、替换
months.splice(4,1)从数组索引为4的位置开始删除一个元素
months.splice(1, 0, 'Feb'); 在数组索引为1的位置添加 元素 “Feb”
months.splice(4, 1, 'May'); 将数组索引为4的元素替换为“May”
reverse()翻转数组
sort()对数组进行排序
arr.sort(function (a, b) {
return b.age - a.age; //对象数组,依据对象中age属性来排序
})
forEach()遍历数组中每一项
filter()筛选出数组中符合条件的项并返回
find()查找数组中是否包含某数,有返回这个数,无返回 undefined
every()判断数组中每一项是否都满足某一条件,满足返回true不满足返回false
reduce()
11-什么是闭包,大量使用闭包的缺点
闭包就是能够读取其他函数内部变量的函数。
例如在 javascript 中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上闭包是将函数内部和函数外部连接起来的桥梁。” 举例:创建闭包最常见方式,就是在一个函数内部创建另一个函数。下面例子中的f2就是闭包
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 //f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
闭包用途
1.一个是前面提到的可以读取函数内部的变量,
2.另一个就是让这些变量的值始终保持在内存中。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000 // 在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。 为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。 这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
12-es6常用的语法有哪些?
1.let、const
let特点:无变量提升、块级作用域、暂时性死区(没有了作用域链)
const特点:无变量提升、块级作用域、声明变量不能修改
2.箭头函数
箭头函数特点
用了箭头函数,this 就不是指向 window,而是父级(指向是可变的)
不能够使用 arguments 对象
不能用作构造函数,这就是说不能够使用 new 命令,否则会抛出一个错误3.promise
01.必须指定一个回调函数
02.创建之后就会立即执行
03.具有不可逆性,状态一旦确定之后不可改变
Promise
翻译过来就是承诺的意思,这个承诺会在未来有一个确切的答复,并且该承诺有三种状态,分别是:
- 等待中(pending)
- 完成了 (resolved)
- 拒绝了(rejected)
13.数组去重的方法有哪些?
// 数组去重的方法 // 方法一 var arr = [1, 2, 3, 4, 5, 3, 5] var newArr = new Set(arr) console.log(newArr); // 方法二 function arrUnique(arr) { var newArr2 = [] for (var i = 0; i < arr.length; i++) { if (newArr2.indexOf(arr[i]) === -1) { newArr2.push(arr[i]) } } return newArr2 } console.log(arrUnique(arr)); // 方法三 let arr = [1, 2, 3, 4, 3, 2, 3, 4, 6, 7, 6]; let unique = (arr) => { return arr.filter((item, index) => { return arr.indexOf(item) === index; }) }; unique(arr); //方法四 let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd'] let myArrayWithNoDuplicates = myArray.reduce(function (previousValue, currentValue) { if (previousValue.indexOf(currentValue) === -1) { previousValue.push(currentValue) } return previousValue }, []) console.log(myArrayWithNoDuplicates)
14.什么是JS的作用域?
作用域就是变量的有效范围。 在一定的空间里可以对数据进行读写操作,这个空间就是数据的作用域1、全局作用域: 最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的;2、局部作用域: 局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部。在 ES6 之前,只有函数可以划分变量的作用域,所以 在函数的外面无法访问函数内的变量3、块级作用域:凡是代码块就可以划分变量的作用域,这种作用域的规则就叫块级作用域
15.什么是链式编程?
链式编程的原理就是返回一个this对象,就是返回本身,达到链式效果。
// 举例一 var arr = [1, 2, 3, 4, 5] console.log(arr.reverse()) // [5,4,3,2,1] 返回反转后的数组 console.log(arr.reverse() === arr); // true console.log(arr.reverse().join()); // 字符串 5,4,3,2,1 // 举例二 Promise.then().catch()
16.如何解决地狱回调的问题?
使用 Promise使用 aync/await
17.如何获取一个对象所有的属性名?如何修改一个对象属性名?
如何获取一个对象所有的属性名?
var obj = { foo: 'bar', baz: 42 }; // 方法一 console.log(Object.keys(obj)); console.log(Object.values(obj)); console.log(Object.entries(obj)); //[ ['foo', 'bar'], ['baz', 42] ] // 方法二 var newArr = [] for (var key in obj) { newArr.push(key) } console.log(newArr);
如何修改一个对象属性名
JSON.stringify()将对象转为json格式字符串,然后 replace方法替换对象属性名,最后再用 JSON.parse()转回对象。
18.请大概讲一下JS的执行机制
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
19.如何实现JS继承?
1-ES6 用过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现,子类必 须 在 constructor 方法中调用 super 方法class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } class Son extends Father { constructor(x, y) { super(x, y); //调用了父类中的构造函数 } }
2-ES5 的继承是通过原型和构造函数机制来实现
// 借用父构造函数继承属性 // 1. 父构造函数 function Father(uname, age) { // this 指向父构造函数的对象实例 this.uname = uname; this.age = age; } Father.prototype.money = function () { console.log(100000); }; // 2 .子构造函数 function Son(uname, age, score) { // this 指向子构造函数的对象实例 Father.call(this, uname, age); this.score = score; } // 子原型对象和父原型对象建立联系,形成原型链,从而继承父原型对象上的方法 // Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化 Son.prototype = new Father(); // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数 Son.prototype.constructor = Son; // 这个是子构造函数专门的方法 Son.prototype.exam = function () { console.log('孩子要考试'); }
20.原型
07-什么是内存泄漏
程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃
不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。
有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。
char * buffer;
buffer = (char*) malloc(42);
// Do something with buffer
free(buffer);
上面是 C 语言代码,
malloc
方法用来申请内存,使用完毕之后,必须自己用free
方法释放内存。这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"(garbage collector)。