1、JS判断数据类型的方法
//基本数据类型
console.log("1" instanceof String); //false
console.log(1 instanceof Number); //false
console.log(true instanceof Boolean); //false
//引用数据类型
console.log([] instanceof Array); //true
console.log(function(){} instanceof Function); //true
console.log({} instanceof Object); //true
console.log(("1").constructor === String); //true
console.log((1).constructor === Number); //true
console.log((true).constructor === Boolean); //true
console.log(([]).constructor === Array); //true
console.log((function(){}).constructor === Function); //true
console.log(({}).constructor === Object); //true
console.log((null).constructor === Null); //报错
console.log((undefined).constructor === Undefined); //报错
function Fn(){};
Fn.prototype=new Array();
var f=new Fn();
console.log(f.constructor===Fn); //false
console.log(f.constructor===Array); //true
console.log(Object.prototype.toString.call(1)); //[object Number]
console.log(Object.prototype.toString.call("1")); //[object String]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call([])); //[object Array]
console.log(Object.prototype.toString.call(function(){})); //[object Function]
console.log(Object.prototype.toString.call({})); //[object Object]
console.log(Object.prototype.toString.call(null)); //[object Null]
console.log(Object.prototype.toString.call(undefined)); //[object Undefined]
2、定义函数的方式:
/第一种
function myFunction(){
}
//定义函数的字符串,函数名本质是变量名,指向某个function的引用。
console.log(myFunction);
//function
console.log(typeof myFunction)
//第二种
var myFunction = new Function(
"num1"
"num2"
"var sum = num1 + num2;return sum"
)
console.log(myFunction(10,20))
3、数组排序:
var arr = [1,36,52,23,48,96,5];
//第一种:
function arrSort(a,b){
return a - b;
}
console.log(arr.sort(arrSort));
//第二种:冒泡排序
//思想:让数组当中相邻的两个数进行比较,数组当中比较小的数值向下沉,数值比较大的向上浮!
// 外层for循环控制循环次数,内层for循环控制相邻的两个元素进行比较。
function bubbleSort(arr){
for(var i = 0;i < arr.length-1;i++){
for(var j = 0;j < arr.length-1-i;j++){
if(arr[j] > arr[j+1]){
swap(arr,j,j+1)
}
}
}
return arr;
}
function swap(arr,i,j){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
console.log(bubbleSort(arr));
//第三种:选择排序
//思想:以起始元素为基准,再从剩余的未排序的元素中挨个与起始元素进行比较,找到剩余未排序元素中
// 最小的一个与之交换位置。重复此步骤。
function selectSort(arr){
for(var i = 0;i < arr.length-1;i++){
for(var j = i+1;j < arr.length;j++){
if(arr[i] > arr[j]){
awap(arr,i,j)
}
}
}
return arr;
}
function swap(arr,i,j){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
console.log(selectSort(arr))
//第四种:选择排序2
function quickSort(arr){
for(var i = 0;i < arr.length-1;i++){
var num = arr[i];
var index = i;
for(var j = i+1;j < arr.length;j++){
if(num > arr[j]){
num = arr[j];
index = j;
}
}
if(index != i){
swap(arr,i,index);
}
}
return arr;
}
function swap(arr,i,j){
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
var arr = [23,56,4,89,556,114,1];
console.log(quickSort(arr));
//第五种:快速排序
//1、从数组中取出一个数作为基准。在原数组中进行移动,将大于基准的数放到基准的右边,小于基准的数放到
// 基准左边,在基准左右形成两个子数组。在左右子数组中反复执行步骤1、2。直到所有子数组只剩下一个数。
function quickSort(arr,i,j){
if(i < j){
let left = i;
let right = j;
let pivot = arr[left];
while(i < j){
while(arr[j] >= pivot && i < j){
j--;
}
if(i < j){
arr[i++] = arr[j];
}
while(arr[i] <= pivot && i < j){
i++;
}
if(i < j){
arr[j--] = arr[i]
}
}
arr[i] = pivot;
quickSort(arr,left,i-1);
quickSort(arr,i+1,right);
return arr;
}
}
let arr = [23,56,4,89,556,114,1];
console.log(quickSort(arr,0,arr.length-1));
//快速排序(for循环)
function quickSort(arr){
//如果数组的长度小于等于1,则直接返回这个数组
if(arr.length <= 1){
return arr;
}
//选择基准数(四舍五入)
var pivotIndex = Math.floor(arr.length/2);
//将基准数与原数组分离
var pivot = arr.splice(pivotIndex,1)[0];
var left = [];
var right = [];
for(var i = 0;i < arr.length;i++){
if(arr[i] <= pivot){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat(pivot,quickSort(right));
}
let arr = [23,56,4,89,556,114,1];
console.log(quickSort(arr));
4、深拷贝和浅拷贝?
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝;
//拷贝1层(测试)------------------------------------------------------------
//Object.assign()拷贝方法
//拷贝{}
a = {name:"张三"};
b = Object.assign({},a);
b.name = "李四"; //拷贝完之后进行改变属性,测试拷贝的属性值改变原数据是否改变。
console.log(a,b);
//输出:{name:"张三"}
{name:"李四"} (拷贝成功)
//拷贝[]
a = ["1","2","3"];
b = Object.assign([],a);
b[1]="hello";
console.log(a,b)
//输出:["1","2","3"]
["1","hello","3"] (拷贝成功)
//for in拷贝方法
var copy = function(a){
var res = a.constructor();
for(var key in a){
if(a.hasOwnProperty(key)){
res[key] = a[key]
}
}
return res;
}
a = {name:"123",people:{name:"456"}};
b = copy(a);
b.people.name="hello";
a = ["a","b","c"];b = copy(a);
b[1] = 0;//拷贝完之后进行改变属性,测试拷贝的属性值改变原数据是否改变。
console.log(a,b)
//输出:["a","b","c"]
["a","0","c"] (拷贝成功)
//拷贝2层(测试)-------------------------------------------------------------
//Object.assign()拷贝方法
a = {name:"abc",people:{name:"张三"}};
b = Object.assign({},a);
b.people.name="李四";
console.log(a,b)
//输出:{name:"abc",people:{name:"李四"}}
{name:"abc",people:{name:"李四"}} (拷贝失败) //for in拷贝方法
var copy = function(a){
var res = a.constructor();
console.log(res);
for(var key in a){
if(a.hasOwnProperty(key)){
res[key] = a[key]
}
}
return res;
}
a = ["a","b",{name:"张三"}];b = copy(a);b[2].name="李四";
console.log(a,b)
//输出:{name:"abc",people:{name:"李四"}}
{name:"abc",people:{name:"李四"}} (拷贝失败)
//JSON.parse(JSON.stringify(obj))方法拷贝2层
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a));
}
var a = {name:"aaa",people:{name:"abc"}};
var b = deepCopy(a);
b.people.name = "def";
console.log(a,b)
//输出:{name:"aaa",people:{name:"abc"}}
{name:"aaa",people:{name:"def"}} (拷贝成功)//JSON.parse(JSON.stringify(obj))方法拷贝3层
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a))
}
var a = [1,2,{name:"aaa"}];
var b = deepCopy(a);
b[2].name = "bbb";
console.log(a,b);
//输出:["1","2",{name:"aaa"}]
["1","2",{name:"bbb"}] (拷贝成功)
//JSON.parse(JSON.stringify(obj))拷贝函数的时候
var deepCopy = function(a){
return JSON.parse(JSON.stringify(a));
}
var a = {name:"aaa",fun:function(){console.log(1)},age:undefined};
var b = deep(a);
b.name = "bbb"
console.log(a,b);
//输出:{name:"aaa",fun:function(){console.log(1)},age:undefined};
{name:"bbb"} (拷贝失败,只拷贝到了name属性)
//JSON.parse(JSON.stringify(obj))拷贝原型链上的属性
function Person(name){
this.name=name;
}
var a = new Person("Bob");
var b = deep(a);
console.log(a.constructor == Person); //true
console.log(b.constructor == Object); //true
//先不说拷贝出的值,光是数据类型已经不同了 (拷贝失败)
console.log(a,b)
//输出:
// Person{name:"Bob"} {name:"Bob"}
深拷贝(完美拷贝):
function deepClone(obj,hash = new WeakMap()){ //递归拷贝
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
if(obj === null || typeof obj !== 'object'){
//如果不是复杂数据类型,直接返回
return obj;
}
if(hash.has(obj)){
return hash.get(obj);
}
//如果obj是数组,那么 obj.constructor 是 [Function: Array]
//如果obj是对象,那么 obj.constructor 是 [Function: Object]
let t = new obj.constructor();
hash.set(obj,t);
for(let key in obj){
//递归
if(obj.hasOwnProperty(key)){ //是否是自身的属性
t[key] = deepClone(obj[key],hash);
}
}
return t;
}
var show = {
name:"Bob",
fun:function(){console.log(1)},
age:null,
pic:undefined,
}
var show2 = deepClone(show);
show2.name="Mary"
console.log(show,show2) //拷贝成功,完美拷贝
5、跨域(怎么解决跨域问题)(跨域是什么)(为什么会有跨域):
6、为什么会有同源策略?
7、原型、原型链、构造函数、实例、继承?
继承:强烈推荐各位去这个网站 github.com/YvetteLau/B…
8、arguments的解释?
function argText(a,b,c){
var actual = arguments.length; //实际传参个数
var hope = argText.length //期望传参个数
console.log(actual,hope);
//转换数组:
var args = [].slice.call(arguments); //第一种
var args = Array.prototype.slice.call(arguments); //第二种
let args = Array.from(arguments); //第三种
let args = [...arguments]; //第四种
console.log(args)
}
argText(1,2)
//输出: 2 3
function argText(){
var e = arguments.callee.toString();
console.log(e);
}
argText();
function argText(){
if(argText.caller){
var caller = argText.caller.toString();
console.log(caller);
}else{
console.log("no caller");
}
}
function handler(){
argText();
}
function copyHandler(){
handler();
}
argText()
//输出: no caller
handler()
//输出: function handler(){argText();}
copyHandler();
//输出: function handler(){argText();}
9、作用域链、闭包、作用域;
//使用闭包找到dome元素的下标
var oLi = document.getElementsByTagName("li");
// 使用闭包后点击对应的li标签,会返回对应的下标
for(var i = 0;i < oLi.length;i++){
//闭包方式一
(function(j){
oLi[j].onclick = function(){
console.log(j)
}
})(i);
//闭包方式二
oLi[i].onclick = function(index){
return function(){
console.log(index);
}
}(i);
//不使用闭包的情况下,输出值全部为(length+1)
oLi[i].onclick = function(){
console.log(i);
}
}
10、ES3~5:
//1、every()
var arr = [1,56,80,5];
var main = arr.every(n => n > 0);
console.log(main) //输出:true
//2、some()
var arr = [1,-56,80,-5];
var main = arr.some(n => n > 0);
console.log(main) //输出:true
//3、reducer()
var arr = [10,20,30,40]
let result = arr.reduce(function(prev,next,index,arr){
return prev + next;
})
console.log(result); //输出:100
11、ES6
//1、Array.from() -- Array.of()
var arrayLink = {
"0":"a",
"1":"b",
"2":"c",
length:3
}
var arr = Array.from(arrayLink)
console.log(arr) // 输出: [a,b,c]
console.log(Array.from("abcdefg")) //输出:["a", "b", "c", "d", "e", "f", "g"]
console.log(Array.of(1,2,3,4,5)) //输出: [1, 2, 3, 4, 5]
//2、copyWithin()
var arr = [1,2,3,4,5];
var main = arr.copyWithin(0,3);
console.log(main); //输出:[4,5,3,4,5]
//3、find()
var arr = [1,-5,2,9,-6];
var main = arr.find(n => n < 0);
console.log(main); //输出:-5
//4、fill()
var arr = ["a","b","c","d"];
console.log(arr.fill(7,1,2));//输出:["a",7,"c","d"]
//5、keys() values() entries()
var arr = ["a","b","c","d"];
for(let index of arr.keys()){
console.log(index);
}
for(let elem of arr.values()){
console.log(elem);
}
for(let [index,elem] of arr.entries()){
console.log(index,elem);
}
//6、includes()
let arr = [12,34,223,45,67]
console.log(arr.includes(45)) //输出:true
[1, 2, NaN].includes(NaN) // true
[1, 2, NaN].indexOf(NaN) // -1
//7、Map
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95
//初始化Map需要一个二维数组,或者直接初始化一个空Map。Map具有以下方法:
var m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.get('Adam'); // undefined
//由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉:
var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
//8、Set
//要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
//重复元素在Set中自动被过滤:
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"} 注意:数字3和字符串'3'是不同的元素
//通过add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果:
s.add(4);
s; // Set {1, 2, 3, 4}
s.add(4);
s; // 仍然是 Set {1, 2, 3, 4}
//通过delete(key)方法可以删除元素:
var s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}
ES6新增数据类型:Symbol
ES10新增数据类型BigInt:BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值
12、ES6解构赋值
let [a,b,c,d] = [1,2,3,4];
console.log(a,b,c,d);
let [x,y,...z] = [1,2,3,4,5,6,7,8];
console.log(x,y,z);
let arr = [1,2,3,4,5];
console.log(...arr); //输出:1 2 3 4 5
let [, , third] = ["foo","bar","baz"];
console.log(third); //输出: baz
let [x,y,...z] = ["a"];
console.log(x,y,z); //输出:"a" undefined []
//报错:
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
let [foo = true] = [];
console.log(foo) //输出: true
let [x = 1] = [undefined];
let [y = 1] = [null];
console.log(x) //输出: 1
console.log(y) //输出: 1
let {foo,bar} = {bar:"acb",foo:"dce"};
console.log(foo,bar); //输出:dce abc
let {baz} = {foo:"aaa",bar:"bbb"};
console.log(baz); //输出:undefined
let {foo:baz} = {foo:"aaa",bar:"bbb"};
console.log(baz); //输出:aaa
console.log(foo); //输出:报错
let x;
{x} = {x:1} //输出:报错
let x;
({x} = {x:1});
console.log(x) //输出:1
const [a,b,c,d,e] = "hello";
console.log(a,b,c,d,e) //输出:h e l l o
let {length : len} = "hello";
console.log(len); //输出: 5
function add([x,y]){
return x + y;
}
console.log(add([1,2])); //输出:3
(重点)解构赋值的用处:
用处1:交换两个变量的值
let a = 10,b = 20;
console.log([a,b] = [b,a]);
用处2:从函数返回多个值
function fn(){
return [1,2,3,4,5];
}
var [a,b,c,d,e] = fn();
console.log(a,b,c,d,e);
用处3:函数参数的定义
function fn3([x,y,z]){
return x+y+z;
}
console.log(fn3([4,5,6]))
用处4:函数参数的默认值
function fn4([x=1,y=2,z=3]){
return x+y+z;
}
console.log(fn4([4,5,6]))
用处5:提取JSON数据
var dataJson = {
"id":1,
"status":"ok",
"data":[1,2,3,4]
}
var {id,status,data:arr} = dataJson;
console.log(id,status,arr);
//输出: 1 "ok" [1,2,3,4]
用处6:遍历Set、Map数据结构
var map = new Map();
map.set("first","hello");
map.set("second","world");
console.log(map);
for(var [key,value] of map){
console.log(key+"is:"+value)
}
用处7:输入模块的指定方法
var {sourceSort,sourceNumber} = require("soure-map")
13、数组去重
var arr = [1,2,45,44,45,2,89,1,1,2,1,2];
第一种:new Set()
var box = Array.from(new Set(arr));
var box = [...new Set(arr)];
console.log(box);
第二种:indexOf()
var box = [];
for(var i = 0;i < arr.length;i++){
if(box.indexOf(arr[i]) == -1){
box.push(arr[i])
}
}
console.log(box);
第三种:splice()
for(var i = 0;i < arr.length;i++){
for(var j = i+1; j < arr.length;j++){
if(arr[i] == arr[j]){
arr.splice(j,1);
}
}
}
console.log(arr);
第四种:sort()+splice()
arr.sort();
for(var i = 0; i < arr.length;i++){
if(arr[i] == arr[i+1]){
arr.splice(i,1);
i--;
}
}
console.log(arr);
第五种:递归函数
Array.prototype.unique = function(){
var arr = this;
len = arr.length;
arr.sort(function(a,b){
return a - b;
})
function loop(index){
if(index >= 1){
if(arr[index] === arr[index-1]){
arr.splice(index,1);
}
loop(index-1);
}
}
loop(len-1);
return arr;
}
var b = arr.unique();
console.log(b);
简单写法如上,还有很多写法,在此不一一列举。
14、ES7新增(不定时添加)
var x = 5 ** 3; // 5的三次方
console.log(x); //输出: 125
15、ES8新增(不定时添加)
var obj = {
name:"Bob",
age:25
}
var arr = Object.entries(obj)
console.log(arr); //输出:[['name', 'Bob'], ['age', 25]]
var arr = ["Bob","Tom"];
var Create = Object.entries(arr);
console.log(Create); //输出:[['0', 'Bob'], ['1', 'Tom']]
var obj = {
name:"Bob",
age:25
}
var arr = Object.values(obj);
console.log(arr); //输出:["Bob", 25]
var obj = {
2:"a",
1:"b",
3:"c"
}
var arr = Object.values(obj);
console.log(arr); //输出:["b", "a", "c"]
var obj = [1,3];
var arr = Object.values(obj);
console.log(arr); //输出:[1,3]
var str = "create"
var newStr1 = str.padStart(10,"x");
var newStr3 = str.padStart(6,"x");
var newStr2 = str.padEnd(10,"x");
var newStr4 = str.padEnd(6,"x");
console.log(newStr1,newStr2,newStr3,newStr4)
//输出:xxxxcreate createxxxx create create
var obj1 = {name:"Bob"};
var obj2 = {age:25};
var newObj = Object.assign(obj1,obj2);
console.log(newObj); //输出: {name: "Bob", age: 25}
16、总结异步编程的6种方式:
//Generator函数:
function* add(){
yield "1";
yield "2";
yield "3";
reutrn;
}
var h = add();
console.log(h.next()); //输出: {value:"1",done:false}
console.log(h.next()); //输出: {value:"2",done:false}
console.log(h.next()); //输出: {value:"3",done:false}
console.log(h.next()); //输出: 报错
//如果去掉return 则,输出:{value: undefined, done: true}
(强烈推荐)原文链接:mp.weixin.qq.com/s/TY6LbYQDy…
17、设计模式(下面只列举了常用10大)
推荐学会前4种,了解后6种
function CreatePerson(){
var obj = new Object();
obj.name = name;
obj.age = age;
obj.sayName = function(){
return this.name;
}
return obj;
}
var p1 = new CreatePerson("Bob",28);
var p2 = new CreatePerson("Tom",25);
var Singleton = function(name){ //单体模式
this.name = name;
this.instance = null;
}
Singleton.prototype.getName = function(){
return this.name;
}
function getInstance(name){ //获取实例对象
if(!this.instance){
this.instance = new Singleton(name);
}
return this.instance;
}
//测试单体模式
var a = getInstance("Bob");
var b = getInstance("Tom");
console.log(a); //输出:Singleton {name: "Bob", instance: null}
console.log(b); //输出:Singleton {name: "Bob", instance: null}
console.log(a === b); //输出:true
var singleMode = (function(){
var privateNum = 112; //创建私有变量
function privateFunc(){
//实现自己业务逻辑的代码
}
//返回一个对象包含公有方法和属性
return {
publicMethod1:publicMethod1,
publicMethod2:publicMethos1,
}
})()
//比如现在京东ceo想送给奶茶妹一个礼物,但是呢假如该ceo不好意思送,或者由于工作忙没有时间送,
//那么这个时候他就想委托他的经纪人去做这件事
var TeaAndMilkGirl = function(name) { // 先申明一个奶茶妹对象
this.name = name;
};
var Ceo = function(girl) { // 这是京东ceo先生
this.girl = girl;
this.sendMarriageRing = function(ring) { // 送结婚礼物 给奶茶妹
console.log("Hi " + this.girl.name + ", ceo送你一个礼物:" + ring);
}
};
var ProxyObj = function(girl){ // 京东ceo的经纪人是代理,来代替送
this.girl = girl;
this.sendGift = function(gift) { // 经纪人代理送礼物给奶茶妹
(new Ceo(this.girl)).sendMarriageRing(gift); // 代理模式负责本体对象实例化
}
};
// 初始化
var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹"));
proxy.sendGift("结婚戒"); // Hi 奶茶妹, ceo送你一个礼物:结婚戒
参考网站:blog.csdn.net/song_mou_xi…
18、Ajax的原生写法:
var xhr; //创建ajax对象
if(window.XMLHttpRequest){ //兼容IE
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open("get",url,true); //建立连接
xhr.send(); //发送
xhr.onreadystatechange = function(){ //获取数据
if(xhr.readyState == 4 && xhr.status == 200){
var data = JSON.parse(xhr.responseText);
}
}
19、图片的懒加载、预加载:
//懒加载简单代码实现
//《JS代码》
window.onload = function(){
//获取当前浏览器视口高度
var viewHeight = document.documentElement.clientHeight;
console.log(viewHeight);
//鼠标滚动回调
function lazyload(){
var img = document.getElementsByClassName("img");
for(let item of img){
//获取每张图片距离顶部的距离
var imgHeight = item.getBoundingClientRect();
console.log(imgHeight)
//判断当图片出现在视口160px的时候把地址放入src中,显示出图片
if(imgHeight.top < (viewHeight - 10)){
item.src = item.getAttribute("data-original");
}
}
}
lazyload(); //页面加载时把当前视口中的图片加载进来
document.addEventListener("scroll",lazyload);
}
//《HTML代码》
<img class="img" lazyload="true" data-origin="图片http地址">
<img class="img" lazyload="true" data-origin="图片http地址"><img class="img" lazyload="true" data-origin="图片http地址"><img class="img" lazyload="true" data-origin="图片http地址"><img class="img" lazyload="true" data-origin="图片http地址">
//预加载简单代码实现
//style
#box{width:0;height:30px;background:red;transition:1s;}
//html
<div id="box"></div>
<ul>
<li><img src="图片http地址"></li>
<li><img src="图片http地址"></li>
<li><img src="图片http地址"></li> <li><img src="图片http地址"></li>
<li><img src="图片http地址"></li> <li><img src="图片http地址"></li> <li><img src="图片http地址"></li> <li><img src="图片http地址"></li>
</ul>
//JS代码
window.onload = function(){
var oBox = document.getElementById("box");
var arr = [
"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3916481728,2850933383&fm=26&gp=0.jpg",
"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=291222564,3369340489&fm=26&gp=0.jpg",
"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974834430,2578081919&fm=26&gp=0.jpg",
"http://img3.imgtn.bdimg.com/it/u=2332284096,307149879&fm=26&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=2667939876,1138687338&fm=26&gp=0.jpg",
"http://img0.imgtn.bdimg.com/it/u=2981065320,3294183154&fm=26&gp=0.jpg",
"http://img4.imgtn.bdimg.com/it/u=2647953258,2919321872&fm=26&gp=0.jpg",
"http://img1.imgtn.bdimg.com/it/u=221171515,1209752772&fm=26&gp=0.jpg"
]
var num = 0;
for(var i = 0; i < arr.length;i++ ){
var oImg = new Image(); //新建一个图片对象
console.log(oImg);
oImg.onload = function(){
num++; //图片预加载完成,执行onload里面的代码
oBox.style.width = num/arr.length * 100 + "%"; //通过图片的加载顺序,来控制进度条的宽度
}
oImg.src = arr[i]; //给图片对象添加路径,注意这条代码必须加在onload后面
}
}
20、防抖、节流
//节流
// 思路:在规定时间内只触发一次
function throttle (fn, delay) {
// 利用闭包保存时间
let prev = Date.now()
return function () {
let context = this
let arg = arguments
let now = Date.now()
if (now - prev >= delay) {
fn.apply(context, arg)
prev = Date.now()
}
}
}
function fn () {
console.log('节流')
}
addEventListener('scroll', throttle(fn, 1000))
//防抖
// 思路:在规定时间内未触发第二次,则执行
function debounce (fn, delay) {
// 利用闭包保存定时器
let timer = null
return function () {
let context = this
let arg = arguments
// 在规定时间内再次触发会先清除定时器后再重设定时器
clearTimeout(timer)
timer = setTimeout(function () {
fn.apply(context, arg)
}, delay)
}
}
function fn () {
console.log('防抖')
}
addEventListener('scroll', debounce(fn, 1000))
21、页面加载进度条的实现
22、this关键字(指向)
情况一:纯粹的函数调用:这是函数的最通常的用法,属于全局调用,因此this就代表全局对象
var x = 1;
function test(){
console.log(this.x);
}
test(); // 1 情况二:作为对象方法
情况二:作为对象方法的调用:函数还可以作为某个对象的方法调用,这时this就指这个上级对象。
function test(){
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m();
情况三:作为构造函数调用:所谓构造函数,就是通过这个函数,可以生成一个新对象。这时this就指这个新对象。
function test(){
this.x = 1;
}
var obj = new test();
console.log(obj.x); // 1
var x = 0;
function test(){
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply(); // 0 this指的是全局对象
obj.m.apply(obj); // 1 this指的是obj
情况五:隐式绑定:函数的调用是在某个对象上触发的,即调用位置上存在上下文对象,典型隐士调用:xxx.fn()
function info(){
console.log(this.age);
}
var person = {
age: 20,
info
}
var age = 28;
person.info(); //20;执行的是隐式绑定
情况六:箭头函数:箭头函数没有自己的this,继承外层上下文绑定的this;
let obj = {
age: 20,
info: function() {
return () => {
console.log(this.age); //this继承的是外层上下文绑定的this
}
}
}
let person = {age: 28};
let info = obj.info();
info(); //20
let info2 = obj.info.call(person);
info2(); //28
23、对象和面向对象
24、函数式编程
25、怎么判断两个对象是否相等?
function compreObj(obj1, obj2) {
var flag = true;
function compre(obj1, obj2) {
if (Object.keys(obj1).length != Object.keys(obj2).length) {
flag = false;
} else {
for (let x in obj1) {
if (obj2.hasOwnProperty(x)) {
if (obj1[x] !== obj2[x]) {
compre(obj1[x], obj2[x]);
}
} else {
flag = false;
}
}
}
if (flag === false) {
return false;
} else {
return true;
}
}
return compre(obj1, obj2)
}
console.log(compreObj(对象1, 对象2));
26、事件模型:事件委托、代理?如何让事件先冒泡后捕获?
//不使用事件捕获
window.onload = function(){
let oBox = document.getElementById("box");
oBox.onclick = function(){
alert(1); //不触发
}
oBox.onclick = function(){
alert(2); //触发
}
}
//使用事件捕获
window.onload = function(){
oBox.addEventListener("click",function(){
alert(1); //触发
})
oBox.addEventListener("click",function(){
alert(2); //触发
})
}
当为事件捕获(useCapture:true)时,先执行body的事件,再执行div的事件
当为事件冒泡(useCapture:false)时,先执行div的事件,再执行body的事件
27、window的onload事件和domcontentloaded
28、for···in和for···of的区别:(for···in取key,for··of取value)
var arr = [99,88,66,77];
for(let i in arr){
console.log(i); //0,1,2,3
}
for(let i of arr){
consoel.log(i); //99,88,66,77
}
var obj = {name:"Bob",age:25};
for(var i in obj){
console.log(i) // name age
}
for(var i of obj){
console.log(i) //报错
}
29、函数柯里化(卡瑞化、加里化)?
function add(x,y){ //普通函数
console.log(x+y);
}
function curryingAdd(x){ //柯里化函数(闭包)
return function(y){
console.log(x+y);
}
}
add(1,2) //3
curryingAdd(1)(2) //3
30、JS预解析?
31、call、applyd的区别,原生JS实现bind。(call,apply,bind三者用法和区别:角度可为参数、绑定规则,运行效率、运行情况)。
function add(a,b){
return a+b;
}
function sub(a,b){
return a-b;
}
var a1 = add.apply(sub,[4,2]); //sub调用add的方法
var a2 = sub.apply(add,[4,2]); //add调用sub的方法
var a3 = add.call(sub,4,2); //sub调用add的方法
console.log(a1); //6
console.log(a2); //2
console.log(a3); //6
//改变this指向
var obj = {
name:"Bob"
}
var name = "Tom";
function test(){
console.log(this.name);
console.log(this);
}
test(); // Tom Window
test.call(obj); //Bob {name:"Bob"}
//借用别的对象的方法
var Person1 = function(){
this.name = "Bob";
}
var Person2 = function(){
this.getName = function(){
console.log(this.name);
}
Person1.call(this);//this指向Person2,结果相当于给Person2加了name属性
}
var person = new Person2();
person.getName(); //Bob
//单纯的函数调用:
function add(){
alert(1);
}
add.call();
32、立即执行函数和使用场景
33、iframe的优缺点有哪些?
34、查找数组重复项
查找该元素首次出现的位置和最后出现的位置下标是否相同,同时判断新数组中是否不存在该元素,如果都满足则添加进新数组中去。
var arr = [1,2,45,44,45,2,89,1,1,2,1,2,44];
Array.prototype.unique = function(){
var arr = this;
var box = [];
for(var str of arr){
if(arr.indexOf(str) != arr.lastIndexOf(str) && box.indexOf(str) == -1){
box.push(str);
}
}
return box;
}
console.log(arr.unique());
35、数组扁平化
var arr = [1,2,[3,4,[5,6,[7,8,[9,10]]]]];
第一种:
function flatten(arr){
var box = [];
arr.map(v => {
if(Array.isArray(v)){
box = box.concat(flatten(v))
}else{
box.push(v);
}
})
return box;
}
console.log(flatten(arr));
第二种(不推荐):
function flatten(arr){
return arr.toString().split(",").map(v => {
return Number(v);
})
}
console.log(flatten(arr));
第三种:
function flatten(arr){
console.log(arr.join(","))
return arr.join(",").split(",").map(v => {
return parseInt(v);
})
}
console.log(flatten(arr));
第四种:
var arr = [1,2,[3,4,[5,6,[7,8,[9,10]]]]];
function flatten(arr){
return arr.reduce((result,item) => {
console.log(result,item)
return result.concat(Array.isArray(item) ? flatten(item) : item);
},[]);
}
console.log(flatten(arr));
第五种:
console.log([].concat(...arr));
function flatten(arr){
while(arr.some(item => Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr));
36、BOM属性对象方法
37、服务端渲染
38、垃圾回收机制
39、eventloop(事件循环):进程和线程,任务队列;

40、如何快速让字符串变成已千为精度的数字
var str = "10000000000";
第一种:把数字转换成字符串后,打散为数组,再从末尾开始,逐个把数组中的元素插入到新数组(result)的开头。 每插入一个元素,counter就计一次数(加1),当counter为3的倍数时,就插入一个逗号,但是要注意开头(i为0时)不需要逗号。最后通过调用新数组的join方法得出结果。
String.prototype.toThousands = function(){
var num = this;
var result = [ ], counter = 0;
num = (num || 0).toString().split('');
for (var i = num.length - 1; i >= 0; i--) {
counter++;
result.unshift(num[i]);
if (!(counter % 3) && i != 0) { result.unshift(','); }
}
return result.join('');
}
console.log(str.toThousands());
第二种:通过正则表达式循环匹配末尾的三个数字,每匹配一次,就把逗号和匹配到的内容插入到结果字符串的开头, 然后把匹配目标(num)赋值为还没匹配的内(RegExp.leftContext)。 如果数字的位数是3的倍数时,最后一次匹配到的内容肯定是三个数字,但是最前面的三个数字前不需要加逗号;如果数字的位数不是3的倍数,那num变量最后肯定会剩下1到2个数字,循环过后,要把剩余的数字插入到结果字符串的开头。
function toThousands(num) {
var num = (num || 0).toString(), re = /\d{3}$/, result = '';
while ( re.test(num) ) {
result = RegExp.lastMatch + result;
if (num !== RegExp.lastMatch) {
result = ',' + result;
num = RegExp.leftContext;
} else {
num = '';
break;
}
}
if (num) { result = num + result; }
return result;
}
console.log(toThousands(str));
第三种:第二种的改良版
function toThousands(num) {
var num = (num || 0).toString(), result = '';
while (num.length > 3) {
result = ',' + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) { result = num + result; }
return result;
}
console.log(toThousands(str));
第四种:懒人版
function toThousands(num) {
return (num || 0).toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
}
console.log(toThousands(str));
41、Promise的解释:
const fn = new Promise(function(resolve,reject){
axios.get(url).then(res => {
resolve(res);
}).catch(err => {
reject(err);
})
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
})
var p1 = Promise.resolve(1),
p2 = Promise.reject(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then((res)=>{
//then方法不会被执行
console.log(results);
}).catch((err)=>{
//catch方法将会被执行,输出结果为:2
console.log(err);
});
let p1 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('1s') //1s后输出
resolve(1)
},1000)
})
let p10 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('10s') //10s后输出
resolve(10) //不传递
},10000)
})
let p5 = new Promise((resolve)=>{
setTimeout(()=>{
console.log('5s') //5s后输出
resolve(5) //不传递
},5000)
})
Promise.race([p1, p10, p5]).then((res)=>{
console.log(res); // 最后输出
})
//结果:
1s
1
5s
10s
42、模块化
// 定义AMD规范的模块
define([function() {
return 模块
})
//私有成员
define(['module1', 'module2'], function(m1, m2) {
let x = 1;
function add() {
x += 1;
return x;
}
return { add };
})
第三种:CMD:CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。AMD 推崇依赖前置,CMD 推崇依赖就近。CMD集成了CommonJS和AMD的的特点,支持同步和异步加载模块。CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的。因此,在CMD中require函数同步加载模块时没有HTTP请求过程。
define(function(require, exports, module) {
// 同步加载模块
var a = require('./a');
a.doSomething();
// 异步加载一个模块,在加载完成时,执行回调
require.async(['./b'], function(b) {
b.doSomething();
});
// 对外暴露成员
exports.doSomething = function() {};
});
// 使用模块
seajs.use('path');
第四种:module:ES6的模块化已经不是规范了,而是JS语言的特性。随着ES6的推出,AMD和CMD也随之成为了历史。ES6模块与模块化规范相比,有两大特点:
//react种运用的就是ES6的模块化
var module1 = value1;
var module2 = value2;
export {module1,module2} //导出模块
import {module1,module2} from "模块路径/模块名" //引入模块
43、new的原理
function _new(){
let target = {}; //创建的新对象
let [constructor,...args] = [...arguments];
//执行[[原型]]连接,target是constructor的实例
target.__proto__ = constructor.prototype;
//执行构造函数,将属性或方法添加到创建的空对象上
let result = constructor.prototype;
if(result && (typeof (result) == "object" || typeof (result) == "function")){
//如果构造函数执行的结构返回的是一个对象,那么返回这个对象
return result;
}
//如果构造函数返回的不是一个对象,返回创建的对象
return target;
}
44、数组和类数组
45、call、apply、bind封装
call函数
// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.mycall = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
context = context || window
context.fn = this
let arg = [...arguments].slice(1)
let result = context.fn(...arg)
delete context.fn
return result
}
apply函数
// 思路:将要改变this指向的方法挂到目标this上执行并返回
Function.prototype.myapply = function (context) {
if (typeof this !== 'function') {
throw new TypeError('not funciton')
}
context = context || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
bind函数
// 思路:类似call,但返回的是函数
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let arg = [...arguments].slice(1)
return function F() {
// 处理函数使用new的情况
if (this instanceof F) {
return new _this(...arg, ...arguments)
} else {
return _this.apply(context, arg.concat(...arguments))
}
}
}
46、如何让(a == 1 && a == 2 && a == 3)的值为true?
方法一:数组的 toString 接口默认调用数组的 join 方法,重新 join 方法
let a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3) //true
方法二:利用数据劫持(Proxy/Object.definedProperty)
let i = 1;
let a = new Proxy({},{
i:1,
get:function(){
return () => this.i++
}
});
console.log(a == 1 && a == 2 && a == 3);
47、var、let和const的区别
①变量提升:var存在变量提升,可以在声明之前使用,而let和const在声明之前使用会报错。
②重复声明:在相同作用域内,let和const不允许重复声明。
③暂时性死区:
var tmp = 123;if(true){ tmp = "abc"; let tmp; //报错}
④初始值:const声明的是一个常量,不可以改变,一旦声明就必须给赋值。const所说的一旦声明值就不能改变,实际上指的是:变量指向的那个内存地址所保存的数据不得改动。