1.JS判断数据类型的方法
① typeof:判断基本数据类型
当变量是:number, string, boolean, function, undefined, object类型时,可以使用typeof进行判断。
当变量是arr, json, null, date, reg, error 类型时全部被错误的检测为object类型。
② instanceof:判断引用数据类型
优点:
//基本数据类型
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
③ constructor:除了undefined和null,其它变量都能使用constructor判断类型。
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); //报错
声明了一个构造函数,并且把他的原型指向了Array的原型。
function Fn(){}; Fn.prototype=new Array(); var f=new Fn(); console.log(f.constructor===Fn); //false console.log(f.constructor===Array); //true
原因:
1、array属于引用型数据,在传递过程中,仅仅是引用地址的传递。
2、每个页面的Array原生对象所引用的地址是不一样的,在子页面声明的array,所对应的构造函数,是子页面的Array对象;父页面来进行判断,使用的Array并不等于子页面的Array;切记,不然很难跟踪问题!
④ Object.prototype.toString.call();
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]
⑤ jquery中的$.type()
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))
- 数组排序:
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));
- 深拷贝和浅拷贝?
浅拷贝:浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。
深拷贝:深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。
深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝;
浅拷贝(只能拷贝一层):Object.assign和for in进行{ }和[ ]的拷贝。
//拷贝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:"李四"}} (拷贝失败)
constructor( ) 是一种用于创建和初始化class创建的对象的特殊方法。
hasOwnProperty( ) 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键);
深拷贝最简单的实现是:JSON.parse ( JSON.stringify ( obj ) ),同时有一定的缺陷:
① 对象的属性值是函数时,无法拷贝
② 原型链上的属性无法拷贝
③ 不能正确的处理Data类型的数据
④ 不能处理RegExp
⑤ 会忽略symbol、undefined
//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以及undefined的字段,而且对data类型的支持也不太友好。
上述方法只能克隆原始对象自身的值,不能克隆他继承的值。
深拷贝(完美拷贝):
① 如果是基本数据类型,直接返回;
② 如果是RegExp或者Data类型,返回对应类型;
③ 如果是复杂数据类型,递归;
④ 考虑循环引用的问题。
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.跨域(怎么解决跨域问题)(跨域是什么)(为什么会有跨域):
造成跨域的原因就是浏览器的同源策略:只要满足协议、主机、端口一致,则两个页面具有相同的源。同源策略限制了从同一个源加载的文档或脚本如何来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要安全机制。(直白:我在一个域名地址下的网页,如果请求一个受到同源策略限制的地址接口的时候,就会报错,这是为了在我的网页下请求别的地址的文件可能是恶意代码,造成不必要的问题。)
解决方法:
① jsonp,允许script加载第三方资源。jsonp是一种非正式的传输协议,该协议的一个要点就是允许用户传递一个callback函数给服务端,然后服务端返回数据的时候会将json数据包裹在这个callback函数中返回。jsonp的本质是利用script标签的src熟悉进行跨域请求,但只能用于get请求。
② 反向代理 (nginx 服务内部配置 Access-Control-Allow-Origin *);
③ cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息
④ iframe 嵌套通讯 (可以与下面的postmessage一起使用)
⑤ window.postmessage ( ),该方法的使用须与iframe嵌套使用。
6.为什么会有同源策略?
JavaScript访问资源,处于安全方面考虑,要限制JS的访问能力,不允许跨域访问资源。如果没有同源限制存在浏览器中的cookie等其他数据可以任意读取,不同域下DOM任意操作,ajax任意请求的话如果浏览了恶意网站那么就会泄漏这些隐私数据。
(再次强调)同源策略:同协议、同域名、同端口。
7.原型、原型链、构造函数、实例、继承?
原型(__proto__):每个对象都有__proto__属性,__proto__指向创建他的构造函数的原型对象(prototype)。
原型链:凡是对象都有一个原型,通过__proto__可以访问原型,访问的原型又是对象,这样依次下去,就会构成一个对象的序列,该结构成为原型链。
(简单明了说法:一个原型对象是另一个原型对象的实例,相关的原型对象层层递进,就构成了实例与原型的链条,就是原型链。当访问一个对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链,当然如果最终找不到返回null。)
构造函数:
① 构造函数的首字母必须大写,用来区分于普通函数;
② 内部使用的this对象,来指向即将要生成的实例对象;
③ 使用new来生成实例对象。
8.arguments的解释?
arguments是一个类似于数组的对象,对应于传递给函数的参数,他有length属性,可以arguments[ i ]来访问对象中的元素,但是它不能用数组的一些方法。例如push、pop、slice等。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 |
每一个函数都会有一个Arguments对象实例arguments,它引用着函数的实参,可以用数组下标的方式" [ ] "引用arguments的元素。arguments.length为函数实参个数,arguments.callee引用函数自身。
arguments对象是所有函数中可用的局部变量,你可以使用arguments对象在函数中引用函数的参数,此参数包含传递给函数的每个参数条目。
arguments.callee:Arguments的callee属性可以调用函数本身,当函数正在执行时才可调用,可以实现方法的递归调用。
function argText(){ var e = arguments.callee.toString(); console.log(e); } argText(); |
arguments.caller:指向调用当前函数的函数
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.作用域链、闭包、作用域;
⑴ 作用域链
定义:一个函数在访问变量的时候,优先使用自己的局部变量,如果没有这个变量的申明,则向上级访问,一直访问到全局。全局都没有的话,语法错误:is not defined。
⑵闭包closure
定义:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部变量,且返回的那个函数在外部被执行,就产生了闭包.闭包是一个环境,具体指的就是外部函数--高阶函数
闭包的特性:
①函数嵌套函数;
②内部函数可以直接访问外部函数的内部变量或参数;
③变量或参数不会被垃圾回收机制回收。
闭包的优点:
①变量长期驻扎在内存中;
②避免全局变量的污染;
③私有成员的存在。
闭包的缺点: 常驻内存,增大内存的使用量,使用不当会造成内存泄漏。
⑶作用域:
全局作用域:window。
局部作用域:函数内部定义的。
//使用闭包找到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:
ES3~5数组常见的方法:
1、concat( ):数组合并。
2、join( ):数组转字符串。
3、pop( ):删除最后一个元素。
4、push( ):数组向后添加。
5、unshift( ):数组向前添加。
6、reverse( ):数组翻转。
7、shift( ):删除第一个元素。
8、slice( ):数组元素的截取,返回一个新数组,新数组是截取的元素,可以为负值。
9、sort( ):对数组元素进行排序;
10、splice( ):删除元素,并向数组添加新元素;
11、toString( ):数组转字符串;
12、toLocaleString( ):将数组转换为本地数组。
13、forEach( ):数组进行遍历;
14、map( ):没有return时,对数组的遍历。有return时,返回一个新数组,该新数组的元素是经过过滤(逻辑处理)过的函数。
15、filter( ):筛选。
16、every( ):当数组中每一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
17、some( ):当数组中有一个元素在callback上被返回true时就返回true。(注:every其实类似filter,只不过它的功能是判断是不是数组中的所有元素都符合条件,并且返回的是布尔值)。
18、reduce( ):回调函数中有4个参数。prev(之前计算过的值),next(之前计算过的下一个的值),index,arr。把数组列表计算成一个单一值 。
//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 |
ES3~5字符串常见的方法:
1、chartAt( ):返回在指定位置的字符;
2、concat( ):字符串连接;
3、indexOf( ):检索字符串,找不到返回-1;
4、slice( ):提取字符串片段,并在新的字符串中返回被提取的部分;
5、split( ):字符串转数组;
6、substr( ):从起始索引号提取字符串中指定数目的字符;
7、substring( ):提取字符串中两个指定的索引号之间的字符;
8、toLowerCase( ):字符串转小写;
9、toUpperCase( ):字符串转大写;
10、valueOf( ):返回某个字符串对象的原始值;
11、trim( ):删除字符串两边的空格;
11.ES6
ES6数组的常用方法:
1、Array.from( ):将对象或字符串转成数组,注意得有length。
2、Array.of( ): 将一组值转换为数组。
3、copyWithin(target,start(可选),end(可选)):数组内数据的复制替换
target:从该位置开始替换数据;
start:从该位置开始读取数据,默认为0;
end:到该位置停止数据的读取,默认为数组的长度
4、find( ):用于找出第一个符合条件的数组成员。
5、findIndex( ):返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
6、fill(value,start,end):使用给定值,填充一个数组。
value:填充的值;
start:开始填充的位置;
end:填充结束的位置。
7、keys( ):对键名的遍历。
8、values( ):对键值的遍历。
9、entries( ):对键值对的遍历。
10、includes( ):数组原型的方法,查找一个数值是否在数组中,只能判断一些简单类型的数据,对于复杂类型的数据无法判断。该方法接受两个参数,分别是查询的数据和初始的查询索引值。
11、flat( ):用于数组扁平,数组去除未定义。
12、flatMap( ):对原数组的每个成员执行一个函数。
13、Map( ):是一组键值对的结构,具有极快的查找速度。
14、Set( ):Set和Map类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。
//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字符串的常用方法:
1、for···of:遍历
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 |
13. ES6解构赋值的用处
用处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") |
14.数组去重
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); |
简单写法如上,还有很多写法,在此不一一列举。
- ES7新增(不定时添加)
- 求幂运算符(**):
var x = 5 ** 3; // 5的三次方 console.log(x); //输出: 125 |
② async、await异步解决方案:async函数返回的是一个 Promise 对象,可以使用 then 方法添加回调函数,async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。await是等待的意思,它等待的是promise 对象的执行完毕,并返回Promise 对象
16.ES8新增(不定时添加)
① Object.entries( ):该方法会将某个对象的可枚举属性与值按照二维数组的方式返回。(如果目标对象是数组,则会将数组的下标作为键值返回)
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']] |
- Object.values():它的工作原理和Object.entries()方法很像,但是它只返回键值对中的值,结果是一维数组。
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] |
③字符串填充padStart()、padEnd():字符串填充方法,该方法可以使得字符串达到固定长度。它有两个参数,字符串目标长度和填充内容。
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 |
④ Object.assign():方法用于对象的合并。
var obj1 = {name:"Bob"}; var obj2 = {age:25}; var newObj = Object.assign(obj1,obj2); console.log(newObj); //输出: {name: "Bob", age: 25} |
17.总结异步编程的6种方式:
① 回调函数;
② 事件监听;
③ 发布订阅模式;
④ Promise;
⑤ Generator(ES6);
⑥ async。
//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} |
- 设计模式(下面只列举了常用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); |
(2)get请求中文参数出现乱码解决方法有两个:
①修改tomcat配置文件添加编码与工程编码一致,如下:
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送你一个礼物:结婚戒 |
⑤ 职责链模式: 消除请求的发送者与接收者之间的耦合。职责链是由多个不同的对象组成的,发送者是发送请求的对象,而接收者则是链中那些接收这种请求并且对其进行处理或传递的对象。请求本身有时候也可以是一个对象,它封装了和操作有关的所有数据。
⑥ 命令模式:命令模式中的命令指的是一个执行某些特定事情的指令。命令模式使用的场景有:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道请求的操作是什么,此时希望用一种松耦合的方式来设计程序代码;使得请求发送者和请求接受者消除彼此代码中的耦合关系。
⑦ 模板方法模式:模板方法模式由二部分组成,第一部分是抽象父类,第二部分是具体实现的子类,一般的情况下是抽象父类封装了子类的算法框架,包括实现一些公共方法及封装子类中所有方法的执行顺序,子类可以继承这个父类,并且可以在子类中重写父类的方法,从而实现自己的业务逻辑。
⑧ 策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
优点:1. 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句。
2. 策略模式提供了开放-封闭原则,使代码更容易理解和扩展。
3. 策略模式中的代码可以复用。
⑨ 发布-订阅者模式:发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
⑩ 中介者模式:中介者模式的作用是解除对象与对象之间的耦合关系,增加一个中介对象后,所有的相关对象都通过中介者对象来通信,而不是相互引用,所以当一个对象发送改变时,只需要通知中介者对象即可。中介者使各个对象之间耦合松散,而且可以独立地改变它们之间的交互。
19.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); } } |
20.图片的懒加载、预加载:
原理:
预加载原理:就是在网页全部加载之前,提前加载图片,当用户需要查看时可直接从本地缓存中渲染,以提供给用户更好的体验,减少等待的时间。
图片懒加载原理(缓载):通过监听onscroll事件判断资源位置,延迟加载图片或符合某些条件时才加载某些图片。首先为所有懒加载的静态资源添加自定义属性字段,比如如果是图片,可以指定data-src为真实的图片地址,src指向loading的图片。 然后当资源进入视口的时候,将src属性值替换成data-src的值。 可以使用元素的getBoundingRect().top判断是否在视口内,也可以使用元素距离文档顶部的距离offsetTop和scrollTop是否小于视口高度来判断
//懒加载简单代码实现 //《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地址"> |