1.延迟加载JS有哪些方式?
1.延迟加载js: async defer
<script defer type="text/javascript" src="./script.js"></script>
2.defer: 等HTML全部解析完成,才会执行js代码,顺序执行
3.async:async和html解析同步(一起的),不是顺序执行代码(谁先加载完先执行)
4.没有加async和defer
5.添加defer
HTML解析过程中会下载js资源
6.添加async
7.我什么时候应该使用什么?
通常,您希望尽可能使用,然后不使用属性。以下是一些需要遵循的一般规则:async defer
- 如果脚本是模块化的并且不依赖于任何脚本,则使用 。
async - 如果脚本依赖于另一个脚本或被另一个脚本所依赖,则使用 。
defer - 如果脚本很小并且被脚本所依赖,则使用在脚本上方没有属性的内联。
asyncscriptasync
2.JS中有哪些数据类型
1.原始数据类型(基本数据类型):
7种:Undefined,Null,Boolean,Number,String,symbol,bigint
2.引用数据类型(复杂数据类型):
Object
3.面试题
1.以下弹出的数据类型
2.如下:
console.log(typeof (NaN));//number
console.log(typeof (undefined));// undefined
console.log(typeof (null));//object
3.NaN是一个数值类型,但不是一个具体的数字。
3.null和defined的区别
- 作者在设计js的都是先设计的null(为什么设计了null:最初设计js的时候借鉴了java的语言)
- null会被隐式转换成0,很不容易发现错误。
- 先有null后有undefined,出来undefined是为了填补之前的坑。
具体区别:JavaScript的最初版本是这样区分的:
- null是一个表示"无"的对象(空对象指针),转为数值时为0;
- undefined是一个表示"无"的原始值,转为数值时为NaN。
4. ==和===有什么不同?
1. == : 比较的是值
- string == number || boolean || number ....都会隐式转换
- 通过valueOf转换(valueOf() 方法通常由 JavaScript 在后台自动调用,并不显式地出现在代码中,返回对象的原始值)
<script>
console.log(1 == '1');//true
console.log(true == 1);//true
console.log(null == undefined);//true
console.log([1, 2] == '1,2');//true
let s = '2';
if (s == 2) {
console.log(typeof s);//string
}
</script>
注: JS 中 valueOf() 方法的详解
- JavaScript 中的 valueOf() 方法用于返回指定对象的原始值,若对象没有原始值,则将返回对象本身。通常由JavaScript内部调用,而不是在代码中显式调用。
- 默认情况下,valueOf 方法由 Object 后面的每个对象继承。 每个内置的核心对象都会覆盖此方法以返回适当的值。
- JavaScript 的许多内置对象都重写了该函数,以实现更适合自身的功能需要。因此,不同类型对象的 valueOf() 方法的返回值和返回值类型均可能不同。
2.=== : 除了比较值,还比较类型
- 如果类型不同,直接返回false。
- 写代码尽量用===
5.JS微任务和宏任务
1.基础知识
- js是单线程的语言。(同一时间只能做一件事)
- js代码执行流程:同步执行完==>事件循环 同步的任务都执行完了,才会执行事件循环的内容 进入事件循环:ajax请求、定时器、事件....
- 事件循环中包含:【微任务、宏任务】
- 微任务:Promise async/await
- 宏任务:setTimeout setInterval Ajax DOM事件
- 微任务比宏任务的执行时间要早
- 要执行宏任务的前提是清空了所有的微任务
流程:同步==>事件循环【微任务和宏任务】==>微任务==>宏任务=>微任务...
2.注意:
宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。
- 宏任务(Macrotask)大概如下:setTimeout setInterval MessageChannel I/O setImmediate(Node环境) script(整体代码块)
- 微任务(Microtask)大概如下:MutationObserver(浏览器环境) promise.[ then/catch/finally ] 事件队列 process.nextTick(Node环境)
面试题一:
for(var i=0;i<3;i++){
setTimeout(function(){
console.log("先执行同步的,再执行异步的.打印结果:",i);
},1000*i);
}
结果:
面试题二:
setTimeout(function(){
console.log('宏任务1');//宏任务
})
new Promise((resolve)=>{
console.log( '同步1:promise 1'); //同步
resolve();
}).then(()=>{
console.log('微1')
}).then(()=>{
console.log('微2')
})
console.log("同步2");//同步
结果:
6.JS作用域考题
1. 除了函数外,js是没有块级作用域。
2. 作用域链:内部可以访问外部的变量,但是外部不能访问内部的变量。
注意:如果内部有,优先查找到内部,如果内部没有就查找外部的。
3. 注意声明变量是用var还是没有写(window.)
4. 注意:js有变量提升的机制【变量悬挂声明】
5. 优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
面试时怎么看:
- 本层作用域有没有此变量【注意变量提升】
- 注意:js除了函数外没有块级作用域
- 普通声明函数是不看写函数的时间顺序的
1.面试题一
// 2.相当于在全局var b=10;
(function () {
var a = b = 10; //1.这b为全局变量,b默认window.b。相当于2.
})()
console.log(b);//10
2.面试题二
function c(){
var b = 1;
function a(){
console.log( b );//undefined。js有变量提升的机制【变量悬挂声明】
var b = 2;
console.log( b );//2
}
a();
console.log( b );//1
}
c();
3.面试题三
var name = 'a';
(function(){//var name;
if( typeof name == 'undefined' ){
var name = 'b';
console.log('111'+name);//111b
}else{
console.log('222'+name);
}
})()
4.面试题四
console.log( a );//undefined
if( false ){
var a = 10;
}
console.log( a );//undefined
var bar = 1;
function test(){
console.log( bar );//undefined
var bar = 2;
console.log( bar );//2
}
test();
5.面试题五
优先级:声明变量 > 声明普通函数 > 参数 > 变量提升
//1.声明变量大于声明普通函数
function fun( a ){
var a = 10;
function a(){}//普通的声明函数
console.log( a );//10
}
fun( 100 );
//2.函数大于参数
function fun( a ){
var a = 10;
var a = function a(){}//声明变量的形式
console.log( a );//f a(){}
}
fun( 100 );
//3.函数大于变量提升
function fun(){ // var a
console.log( a );//f a(){}
var a = 20;
function a(){}
}
fun();
//4.参数大于变量提升
function fun(a){ // var a
console.log( a );//a
var a = 20;
function a(){}
}
fun(100);
//5.变量的再次赋值
function fun(){ // var a
a = 10;
console.log( a );//10
var a = 20;//变量的再次赋值
console.log( a );//20
}
fun();
7.JS对象考题
1.js对象注意点
- 对象是通过new操作符构建出来的,所以对象之间不相等(除了引用外);
console.log( [1,2,3] === [1,2,3] );//false
- 对象注意:引用类型(共同一个地址);
var obj1 = {
a: 1
}
var obj2 = obj1;//obj1对象引用的地址赋值给了obj2
obj1.a = 'aaaaa';
console.log(obj1, obj2);//{ a: 'aaaaa' } { a: 'aaaaa' }
var arr1 = [1, 2, 3];
var arr2 = arr1;
console.log(arr1 === arr2);//true
3. 对象的key都是字符串类型;
var o = {
b: 'bbbbbb'
}
var obj1 = {
a: 1,
'张三': '你好'
}
obj1[o] = '123';//给obj1添加一个o进来
for (var k in obj1) {
console.log(k);
}
结果
var a = {}
var b = {
key: 'a'
}
var c = {
key: 'c'
}
// [object Object]
a[b] = '123';//相当于 var a = {[object Object]:123 }
a[c] = '456';//会覆盖前面的{[object Object]:123 }变成{[object Object]:456}
console.log(a[b]); // 456
for (var k in a) {
console.log(k)//[object Object]
console.log(typeof k)//string
}
- 对象如何找属性|方法;
查找规则:先在对象本身找 ===> 构造函数中找 ===> 对象原型中找 ===> 构造函数原型中找 ===> 对象上一层原型查找
注:
new Array是直接创建一个数组,平时使用的时候还能省去new关键词来使用
function Fun() {
this.a = '在Fun函数中添加的';
}
Fun.prototype.a = '这是Fun原型添加的';
let obj = new Fun();
// console.log(Fun.prototype == obj._proto_);
obj.a = '对象本身';
obj.__proto__.a = '这是对象原型添加的';
Object.prototype.a = '这是Object添加的'
// 1.obj.a 会先找对象本身-没有-->则查找在函数中添加的-->查找对象原型中添加的-->Fun函数中添加的-->对象中添加的
console.log(obj.a);
2.面试题
var obj1 = {
a:'hellow'
}
var obj2 = obj1;
obj2.a = 'world';
console.log(obj1); //{a:world}
(function(){
console.log(a); //undefined
var a = 1;
})();
8.JS作用域+this指向+原型 考题
1.对于函数的理解
function fun() {
// this.xxx = '12334',t添加之后,console.log(new fun())返回1111 和fun {xxx: '12334'}
console.log("1111");
return 'aaaa';
}
console.log(fun);/*打印的函数体,ƒ fun(){console.log("1111");return 'aaaa';}*/
console.log(fun());//1111 aaaa
console.log(new fun())//1111和返回一个对象fun{}
2.调用foo()的结果
function Foo() {
getName = function () { console.log(1) } //注意getName是全局window.的
return this;
}
Foo.getName = function () { console.log(2) }
Foo.prototype.getName = function () { console.log(3) }
var getName = function () { console.log(4) }
function getName() {
console.log(5)
}
// 1.情况一:加()
// 二、在这里会执行getName = function () { console.log(1) },覆盖前面的var getName = function () { console.log(4) }
Foo().getName(); //1 一、,Foo加()就会运行21,22行,返回window。相当于window.getName()
getName(); //1
3.考题一
function Foo() {
getName = function () { console.log(1) } //注意getName是全局window.的
return this;
}
Foo.getName = function () { console.log(2) }
Foo.prototype.getName = function () { console.log(3) }
var getName = function () { console.log(4) }
function getName() {
console.log(5)
}
/*情况二*/
Foo.getName(); //2
getName(); //4,声明的变量大于普通函数
Foo().getName(); //1
getName(); //1
// 对象查找一个属性方法,现在他本身找,--》构造函数找--》对象的原型找--》构造函数的原型找--》原型链里去找。
//一、先看new Foo()本身有没有--》在去Foo的构造函数里面找,而foo里的getName是全局window.的,不是this.的--》去对象原型(__proto__)没有,但是对象函数原型和构造函数原型是一个原型,构造函数原型(Foo.prototype.getName = function () { console.log(3) }
new Foo().getName();//3,
4.考题二
1.头条考题一
<script type="text/javascript">
//头条考题
var o = {
a:10,
b:{
a:2,
fn:function(){
console.log( this.a ); // 2
console.log( this ); //代表b对象 {a: 2, fn: ƒ}
}
}
}
o.b.fn();//b执行的fn,fn里的this代表的b
</script>
2.头条考题二
<script type="text/javascript">
//头条考题
window.name = 'ByteDance';
function A() {
this.name = 123;
}
A.prototype.getA = function () {
console.log(this);//this代表window
return this.name + 1;
}
let a = new A();
console.log(a.getA);/*ƒ () {console.log(this);return this.name + 1;}*/
let funcA = a.getA;//a.getA,返回的是函数体,相当于把函数赋给了funA
funcA(); //ByteDance1
</script>
3.头条考题三
<script type="text/javascript">
//头条考题
var length = 10;
function fn() {
console.log(this);//这里的this代表window
return this.length + 1;
}
var obj = {
length: 5,
test1: function () {
return fn();
}
}
obj.test2 = fn;/*相当于obj.test2 =function(){return this.length + 1;}*/
//
console.log(obj.test1()); //1
// fn()结果11,obj.test2()结果6
console.log(fn() === obj.test2()); //false
// obj.test1()结果为11,obj.test2()结果为6
console.log(obj.test1() == obj.test2()); //false
</script>
9.JS判断变量是不是数组,你能写出哪些方法?
1.方式一:isArray
var arr = [1,2,3];
console.log( Array.isArray( arr ) );//true
2.方式二:instanceof 【可写,可不写】
var arr = [1,2,3];
console.log( arr instanceof Array );//true
3.方式三:原型prototype
var arr = [1,2,3];
console.log( Object.prototype.toString.call(arr).indexOf('Array') > -1 );//true
4.方式四:isPrototypeOf()
var arr = [1,2,3];
console.log( Array.prototype.isPrototypeOf(arr) )//true
5.方式五:constructor
var arr = [1,2,3];
console.log( arr.constructor.toString().indexOf('Array') > -1 )//true
<script type="text/javascript">
var str = '你好';
var arr = [1, 2, 3];
console.log(Array.isArray(arr));//true
console.log(arr instanceof Array);//true
console.log(Object.prototype.toString.call(arr));//[object Array]
console.log(Object.prototype.toString.call(arr).indexOf('Array') > -1);//true
console.log(Array.prototype.isPrototypeOf(arr))
console.log(arr.constructor.toString().indexOf('Array') > -1)
</script>
10.面试题:slice是干嘛的、splice是否会改变原数组
1. slice是来截取的
从已有的数组中返回选定的元素
提取数组中的某一部分,并以新的字符串返回被提取的部分
slice方法不会改变原始数组
- 参数可以写slice(3)、slice(1,3)、slice(-3)
- 返回的是一个新的数组
- array.slice(start, end)
-
start 可选,规定从何处开始选取。如果该参数为负数,则表示从原数组中倒数第几个元素开始提取。
-
end 可选,规定从何处结束选取。该参数是数组片段结束处的数组下标,截取的片段不包含改元素。
-
返回值: 返回一个新的数组,包含从start(包括该元素) 到end(不包括该元素)的arrayObject中的元素。
var arr1 = ['a', 'b', 'c', 'd', 'e'];
var arr2 = arr1.slice(1, 3);//从下标为1开始截取3之间的有元素,不包括下标3的元素
console.log(arr1);//slice不会改变原数组
console.log(arr2);//['b', 'c']
var arr3 = arr1.slice(-3);
console.log(arr3);//['c', 'd', 'e']
2. splice 功能有:插入、删除、替换
- 返回:删除的元素
- 该方法会改变原数组
var arr3 = ['a', 'b', 'c', 'd', 'e'];
var arr4 = arr3.splice(1, 1);
console.log("arr4", arr4);////['b']返回删除到哪里的内容
console.log("arr3", arr3);//['a', 'c', 'd', 'e']
var arr3 = ['a', 'b', 'c', 'd', 'e'];
var arr4 = arr3.splice(1, 1, '你好');//从索引为1的位置,删除1个,插入'你好'
console.log(arr4, arr3);//['b']和['a', '你好', 'c', 'd', 'e']
11.JS数组去重
1.方式一:new Set()
因为Set数据结构并非真正的数组,它类似于数组,并且成员值都是唯一的,没有重复,所以可以用来做去重操作。但是因为它是一个类似数组结构,所以需要转型为真正的数组去使用。所以需要用Array.from。
var arr1 = [1, 2, 3, 2, 4, 1];
console.log(new Set(arr1));//Set(4) {1, 2, 3, 4}
console.log(Array.from(new Set(arr1)));//Array.from把像数组的内容转化成数组
console.log([...new Set(arr1)]);//扩展运算符展开
// 封装成函数
function unique(arr) {
return [...new Set(arr)]
}
console.log(unique(arr1));//[1, 2, 3, 4]
2.方式二:indexOf
var arr2 = [1, 2, 3, 2, 4, 1];
function unique(arr) {
var brr = [];
for (var i = 0; i < arr.length; i++) {
if (brr.indexOf(arr[i]) == -1) {//如果没有找到,把arr[i]的内容push到brr中
brr.push(arr[i]);
}
}
return brr;
}
console.log(unique(arr2));////[1, 2, 3, 4]
3.方式三:sort
var arr3 = [1, 2, 3, 2, 4, 1];
function unique(arr) {
arr = arr.sort();//将数组排序
var brr = [];
for (var i = 0; i < arr.length; i++) {
if (arr[i] !== arr[i - 1]) {
brr.push(arr[i]);
}
}
return brr;
}
console.log(unique(arr3));//[1, 2, 3, 4]
12.找出多维数组最大值
1.forEach() 方法对数组的每个元素执行一次提供的函数。
<script type="text/javascript">
function fnArr(arr) {
var newArr = [];
// forEach() 方法对数组的每个元素执行一次提供的函数。
arr.forEach((item, index) => {
// 扩展运算符(...)是ES6的语法,用于取出参数对象的所有可遍历属性,然后拷贝到当前对象之中。
newArr.push(Math.max(...item))
})
return newArr;
}
console.log(fnArr([
[4, 5, 1, 3],
[13, 27, 18, 26],
[32, 35, 37, 39],
[1000, 1001, 857, 1]
]));//[5, 27, 39, 1001]
</script>
13.面试题:给字符串新增方法实现功能
1.题目:
给字符串对象定义一个addPrefix函数,当传入一个字符串str时,它会返回新的带有指定前缀的字符串,例如:
console.log( 'world'.addPrefix('hello') ) 控制台会输出helloworld
//一、
var str = '你好'
String.prototype.addPrefix = function (str) {
console.log(this);//String {'你好'}
// return str + this;
}
console.log(str.addPrefix())
//二
var str = '你好'
String.prototype.addPrefix = function (str) {
console.log(this);//这里的this指的是{'world'}
return str + this;
}
console.log('world'.addPrefix('hello'))//helloworld
14.面试题:找出字符串出现最多次数的字符以及次数
var str = 'aaabbbbbccddddddddddx';
var obj = {};
for (var i = 0; i < str.length; i++) {
var char = str.charAt(i);//charAt() 返回指定索引位置的字符
console.log(char);
if (obj[char]) {
obj[char]++;
} else {
obj[char] = 1;
}
}
console.log(obj);//{a: 3, b: 5, c: 2, d: 10, x: 1}
//统计出来最大值
var max = 0;
for (var key in obj) {
if (max < obj[key]) {
max = obj[key];
}
}
//拿最大值去对比
for (var key in obj) {
if (obj[key] == max) {
console.log('最多的字符是' + key);//d
console.log('出现的次数是' + max);//10
}
}
15.面试题:new操作符具体做了什么
- 创建了一个空的对象
- 将空对象的原型,指向于构造函数的原型
- 将空对象作为构造函数的上下文(改变this指向)
- 对构造函数有返回值的处理判断
function Foo(){
// console.log(this);
this.name = '张三'
// return [1,2,3];//如果返回的是基本类型,则忽略,如果返回的是引用类型,如:对象,数组,则直接返回
}
console.log(Foo());//运行Foo(),Foo里面的this执行window
// Foo();
// 1. 创建了一个空的对象
console.log( new Foo() );//new Foo()之后,Foo里面的this指向Foo {}对象
// 2.将空对象的原型,指向于构造函数的原型
console.log( Foo.prototype == new Foo().__proto__ );//true
function Fun( age,name ){
this.age = age;
this.name = name;
}
function create( fn , ...args ){
//1. 创建了一个空的对象
var obj = {}; //var obj = Object.create({})
//2. 将空对象的原型,指向于构造函数的原型
// Object.setPrototypeOf(),为现有对象设置原型,返回一个新对象
// 接收两个参数:第一个是现有对象,第二是原型对象。
Object.setPrototypeOf(obj,fn.prototype);
//3. 将空对象作为构造函数的上下文(改变this指向)
// apply作用 作用有两个,跟它的入参有关。
// 改变this指向。
// 将数组入参变为一般入参
var result = fn.apply(obj,args);
//4. 对构造函数有返回值的处理判断
return result instanceof Object ? result : obj;
}
console.log( create(Fun,18,'张三') )
16.面试题:闭包
1. 闭包是什么
闭包是一个函数加上到创建函数的作用域的连接,闭包“关闭”了函数的自由变量。
2. 闭包可以解决什么问题【闭包的优点】
- 2.1 内部函数可以访问到外部函数的局部变量
- 2.2 闭包可以解决的问题
var lis = document.getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
(function(i){
lis[i].onclick = function(){
alert(i);
}
lis[i] = null;
})(i)
}
3. 闭包的缺点
- 3.1 变量会驻留在内存中,造成内存损耗问题。 解决:把闭包的函数设置为null
- 3.2 内存泄漏【ie】 ==> 可说可不说,如果说一定要提到ie
17.面试题:原型链
1. 原型可以解决什么问题
对象共享属性和共享方法
2. 谁拥有原型
- 函数拥有:prototype
- 对象拥有:__ proto __
function Fun() {
//this.run = '1'
}
console.log(Fun.prototype);//constructor: ƒ}
var obj = new Fun();
console.log(obj.__proto__);//constructor: ƒ}
console.log(Fun.prototype == obj.__proto__);//true
3. 对象查找属性或者方法的顺序
先在对象本身查找 --> 构造函数中查找 --> 对象的原型 --> 构造函数的原型中 --> 当前原型的原型中查找
4. 原型链
4.1 是什么?:就是把原型串联起来
4.2 原型链的最顶端是null
<script type="text/javascript">
function Fun() {
// 2.到创建对象的构造函数中查找
this.run = '1'
}
// console.log(Fun.prototype);//constructor: ƒ}
//Fun.prototype.run = '2'
var obj = new Fun();
// console.log(obj.__proto__);//constructor: ƒ}
// console.log(Fun.prototype == obj.__proto__);//true
// 1.对象本身查找
obj.run = '3';
// 3.对象的原型中查找
obj.__proto__.run = '4'
// 4.当前原型的原型中查找
Object.prototype.run = '5';
console.log(obj.run);
console.log("当前对象的原型:",Fun.prototype, obj.__proto__)
console.log("当前对象的原型的原型:",obj.__proto__.__proto__);
</script>
18.面试题: JS继承有哪些方式
- 方式一:ES6
//方式1
// 定义父类
class Parent{
constructor(){
this.age = 18;
}
}
// 定义子类,继承父类
class Child extends Parent{
constructor(){
super();//super关键字可以在子类的构造方法中显示地调用父类的构造方法,
//super()必须为子类构造函数中的第一行.
this.name = '张三';
}
}
let o1 = new Child();
console.log( o1,o1.name,o1.age );//Child {age: 18, name: '张三'}age: 18name: "张三"[[Prototype]]: Parent '张三' 18
2.方式二:原型链继承,实现共享。
// 方式2
function Parent(){
this.age = 20;
}
function Child(){
this.name = '张三'
}
Child.prototype = new Parent();//原型链继承
let o2 = new Child();
console.log( o2,o2.name,o2.age );//Child {name: '张三'} '张三' 20
3.方式三:借用构造函数继承,实现不了共享。
//方式3
function Parent(){
this.age = 22;
}
function Child(){
this.name = '张三'
Parent.call(this);//让this指向,指向当前。
}
let o3 = new Child();
console.log( o3,o3.name,o3.age );//Child {name: '张三', age: 22} '张三' 22
4.方式四:组合式继承,即可以实现共享,又可以解决原型链继承的一些问题。
//方式4
function Parent(){
this.age = 100;
}
function Child(){
Parent.call(this);//让this指向,指向当前
this.name = '张三'
}
Child.prototype = new Parent();//原型链继承
let o4 = new Child();
console.log( o4,o4.name,o4.age );//Child {age: 100, name: '张三'} '张三' 100
19.面试题:说一下call、apply、bind区别
1.共同点:功能一致
可以改变函数体内的this指向
语法: 函数.call()、函数.apply()、函数.bind()
2. 区别:
1. call、apply可以立即执行。bind不会立即执行,因为bind返回的是一个函数需要加入()执行。
2. 参数不同:apply第二个参数是数组。call和bind有多个参数需要挨个写。
fun.call()
var str = '你好';
var obj = {str:'这是obj对象内的str'}
function fun( name, age ){
// this.name = name;
// this.age = age;
console.log("fun的this:", this , this.str );
}
//call立即执行,函数体fun内的this指向obj
fun.call( obj ); //fun的this: {str: '这是obj对象内的str'} 这是obj对象内的str
// fun.apply( obj ); //apply立即执行
//fun.bind( obj ); //bind不会立即执行,因为bind返回的是函数
// fun.call(obj,'张三',88);
// fun.apply(obj,['张三',88]); //apply的第二参数是数组,才生效
//fun.bind(obj,'张三',88)(); //加括号才执行
fun();//Window {window: Window, self: Window, document: document, name: '', location: Location, …}
3. 场景:
- 用apply的情况
var arr1 = [1,2,4,5,7,3,321];
console.log(Math.max(arr1));//NaN
console.log( Math.max.apply(null,arr1) )//321
- 用bind的情况
var btn = document.getElementById('btn');
var h1s = document.getElementById('h1s');
btn.onclick = function(){
console.log( this.id );//hls
}.bind(h1s)//.bind(hls),让this指向hls
20.面试题:sort背后原理是什么?
1.sort()方法
sort() 方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。
语法:array.sort(sortby);参数sortby可选。规定排序顺序。必须是函数。
注:如果调用该方法时没有使用参数,将按字母顺序对数组中的元素进行排序,说得更精确点,是按照字符编码的顺序进行排序。要实现这一点,首先应把数组的元素都转换成字符串(如有必要),以便进行比较。
// 方式一:
var arr1 = [12,11,1,23,'45','34',21,'b','a','ab','bc'];
console.log( arr1.sort() );//11) [1, 11, 12, 21, 23, '34', '45', 'a', 'ab', 'b', 'bc']
// 方式二:
var arr2 = [12,111,11,1,23,'45','34',21];
var arr3 = arr2.sort(function( a, b ){
return a-b;//从小到大进行排序
})
console.log( arr3 );//[1, 11, 12, 21, 23, '34', '45', 111]
// 方式三:
var arr4 = [
{name:'zhangsan',age:18},
{name:'lisi',age:2},
{name:'wangwu',age:50},
];
function compare(age){
return function(a,b){
var val1 = a[age];
var val2 = b[age];
return val1 - val2;
}
}
var arr5 = arr4.sort(compare('age'));
console.log( arr5 );
/*
0: {name: 'lisi', age: 2}
1: {name: 'zhangsan', age: 18}
2: {name: 'wangwu', age: 50}
*/
2.原理
V8 引擎 sort 函数只给出了两种排序 InsertionSort 和 QuickSort,数量小于10的数组使用 InsertionSort,比10大的数组则使用 QuickSort。
之前的版本是:插入排序和快排,现在是冒泡
原理实现链接:github.com/v8/v8/blob/…
710行
21.面试题:深拷贝和浅拷贝
共同点:复制
1. 浅拷贝:只复制引用,而未复制真正的值,所以一个改变,另一个也会改变。
//1. 浅拷贝,会互相影响
var arr1 = ['a','b','c','d'];
var arr2 = arr1;
arr1[0] = '你好吗';
arr2[1] = '还行';
/*['你好吗', '还行', 'c', 'd'] (4) ['你好吗', '还行', 'c', 'd']*/
console.log( arr1, arr2 );
var obj1 = {a:1,b:2}
var obj2 = Object.assign(obj1);//对象属性的合并
obj1.a = '100';
obj2.b = '你怎么养';
console.log( obj1,obj2);//{a: '100', b: '你怎么养'} {a: '100', b: '你怎么养'}
2.深拷贝:是复制真正的值,即复制真正独立的一份。
//2. 深拷贝:是复制真正的值,即复制真正独立的一份。(不同引用)
var obj3 = {
a:1,
b:2
}
// JSON.stringify 方法将某个对象转换成 JSON 字符串形式
// JSON.parse()【从一个字符串中解析出json对象】
var obj4 = JSON.parse(JSON.stringify( obj3 ));
obj3.a = '100';
obj4.b = '你怎么阳';
console.log( obj3, obj4 );//{a: '100', b: 2} {a: 1, b: '你怎么阳'}
递归形式
var obj5 = {
a:1,
b:2,
arr:['a','b','c','d']
}
function copyObj( obj ){
if( Array.isArray(obj) ){
var newObj = [];
}else{
var newObj = {};
}
for( var key in obj ){
if( typeof obj[key] == 'object' ){
newObj[key] = copyObj(obj[key]);
}else{
newObj[key] = obj[key];
}
}
return newObj;
}
console.log( copyObj(obj5) );//{a: 1, b: 2, arr: Array(4)}
辨明. for in 和 for of 的区别
- for...in 循环:只能获得对象的键名,不能获得键值
- for...of 循环:允许遍历获得键值
var arr = ['red', 'green', 'blue']
for (let item in arr) {
console.log('for in item', item)
}
/*
for in item 0
for in item 1
for in item 2
*/
for(let item of arr) {
console.log('for of item', item)
}
/*
for of item red
for of item green
for of item blue
*/
22.面试题:localstorage、sessionstorage、cookie的区别
一. 公共点:在客户端存放数据
二. 区别:
- 数据存放有效期
- sessionStorage : 仅在当前浏览器窗口关闭之前有效。【关闭浏览器就没了】
- localStorage : 始终有效,窗口或者浏览器关闭也一直保存,所以叫持久化存储。
- cookie : 只在设置的cookie过期时间之前有效,即使窗口或者浏览器关闭也有效。
- localStorage、sessionStorage不可以设置过期时间。 cookie 有过期时间,可以设置过期(把时间调整到之前的时间,就过期了)
- 存储大小的限制
- cookie存储量不能超过4k
- localStorage、sessionStorage不能超过5M
根据不同的浏览器存储的大小是不同的。
sessionStorage.setItem('key','123');
localStorage.setItem('key','456');
created(){
let date=1000,
let time=1000*60*60*24;
time=date.getTime()+time;
document.cookie='name=789;expires='+date.toUTCString()+'';
}