目录
-
闭包
-
如何解决回调地狱
-
事件委托
-
说一下图片的懒加载和预加载
-
mouseover和mouseenter的区别
-
js的new操作符做了哪些事情
-
改变函数内部this指针的指向函数(bind,apply,call的区别)
-
js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
-
异步加载JS的五种方式
-
js节流和防抖
-
js垃圾回收机制
-
Commonjs、AMD和CMD
-
浅拷贝与深拷贝
-
将原生的ajax封装成promise
-
实现两列等高
-
代码的执行顺序
-
如何实现sleep的效果
-
js判断数据类型
-
js数组常用方法
-
数组去重
-
性能优化
-
JS的语言特性
-
判断变量是数组Array类型
31. echarts底层原理
1. 闭包
闭包是指有权访问另一个函数作用域中的变量的函数;
function aaa(){
var name = "xxx"
return function bbb(){
alert(name);
}
}
常见的定时器问题
for( var i = 0; i < 5; i++ ) {
setTimeout(() => {
console.log( i );
}, 1000)
}
setTimeout函数在当前执行队列的最后执行,获取到的i是最外部作用域的i=5,所以得到5个5
那么如果想1s后得到0-4怎么做?在for循环内创建闭包
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
},1000)
})(i)
}
同样setTimeout在执行队列的最后执行,获取到的j是外部函数的j,由于闭包中的变量会保存下来,每一次获取的j分别是0,1,2,3,4
由于闭包会常驻内存,使用不当会导致内存溢出
2. 继承
3. 如何解决回调地狱
1. 拆解function:将各步拆解为单个的function
function buildCatList(list, returnVal, fn) {
setTimeout(function (name) {
var catList = list === '' ? name : list + ',' + name
fn(catList)
}, 200, returnVal)
}
buildCatList('', 'Panther', getJanguar)
function getJanguar(list) {
buildCatList(list, 'Janguar', getLynx)
}
function getLynx(list) {
buildCatList(list, 'Lion', print)
}
function print(list) {
console.log(list)
}
// Panther,Janguar,Lion
2. 事件发布/监听模式
一方面,监听某一事件,当事件发生时,进行相应回调操作;另一方面,当某些操作完成后,通过发布事件触发回调,这样就可以将原本捆绑在一起的代码解耦。
3.通过 Promise 链式调用的方式
function buildCatList(list, returnVal) {
return new Promise(function (resolve, reject) {
setTimeout(function (name) {
var catList = list === '' ? name : list + ',' + name
resolve(catList)
}, 200, returnVal)
})
}
buildCatList('', 'Panther').then(function (res) {
return buildCatList(res, 'Janguar')
}).then(function (res) {
return buildCatList(res, 'Lion')
}).then(function (res) {
console.log(res)
})
// Panther,Janguar,Lion
Promise 函数虽然改变了之前回调地狱的写法,但是在根本上还是函数套函数,看起来不是那么的美观
4.通过 Generator 函数暂停执行的效果方式
function getPanther(list) {
return list + 'Panther' + ','
}
function getJaguar(list) {
return list + 'Jaguar' + ','
}
function getLion(list) {
return list + 'Lion'
}
function* buildCatList() {
yield getPanther
yield getJaguar
yield getLion
}
//流程控制
function run(fn){
const gen = fn() // 调用 Generator 函数,返回一个遍历器对象 (Iterator)
var a = gen.next().value('') // 调用遍历器对象的 next() 方法,就会返回一个有着 value 和 done 两个属性的对象
var b = gen.next().value(a)
var c = gen.next().value(b)
console.log(c)
}
run(buildCatList) // 开始执行
// Panther,Jaguar,Lion
通过 generator 虽然能提供较好的语法结构,但是毕竟 generator 与 yield 的语境用在这里多少还有些不太贴切
5.通过ES8的异步函数 async / await
- async 表示这是一个 async 函数,await 只能用在这个函数里面
- await 表示在这里等待 Promise 返回结果后,再继续执行
- await 后面跟着的应该是一个 Promise 对象(当然,其他返回值也没关系,只是会立即执行)
- await 等待的虽然是 Promise 对象,但不必写 .then() ,可以直接得到返回值
function buildCatList(list, returnVal) {
return new Promise(function (resolve, reject) {
setTimeout(function (name) {
var catList = list === '' ? name : list + ',' + name
resolve(catList)
}, 200, returnVal)
})
}
function fn(list) {
return list + ',' + 555
}
async function render() {
var a = await buildCatList('','Panther')
var b = await buildCatList(a, 'Jaguar')
var c = await buildCatList(b, 'Lion')
var d = await fn(c)
console.log(d)
}
render()
// Panther,Jaguar,Lion,555
代码简洁清晰,异步代码也具有了“同步”代码的结构
4. 事件委托
简介:事件委托指的是,不在事件的发生地(直接dom)上设置监听函数,而是在其父元素上设置监听函数,通过事件冒泡,父元素可以监听到子元素上事件的触发,通过判断事件发生元素DOM的类型,来做出不同的响应。
举例:最经典的就是ul和li标签的事件监听,比如我们在添加事件时候,采用事件委托机制,不会在li标签上直接添加,而是在ul父元素上添加。
好处:比较合适动态元素的绑定,新添加的子元素也会有监听函数,也可以有事件触发机制。
5. 说一下图片的懒加载和预加载
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。
懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
6. mouseover和mouseenter的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
7. js的new操作符做了哪些事情
new共经过了4个阶段
1.创建一个空对象
var obj = new Object();
2. 设置原型链(当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象)
obj.__proto__= Func.prototype;
3.让Func中的this指向obj,并执行Func的函数体。(创建新的对象之后,将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
var result =Func.call(obj);
4. 判断Func的返回值类型:
如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象
if (typeof(result) == "object"){
func=result;
}
else{
func=obj;;
}
默认情况下函数返回值为undefined,即没有显示定义返回值的话,但构造函数例外,new构造函数在没有return的情况下默认返回新创建的对象。
但是,在有显示返回值的情况下,如果返回值为基本数据类型{string,number,null,undefined,Boolean},返回值仍然是新创建的对象。
只有在显示返回一个非基本数据类型时,函数的返回值才为指定对象。在这种情况下,this所引用的值就会被丢弃了
8.改变函数内部this指针的指向函数(bind,apply,call的区别)
call()的用法
var obj = {
text: '我的两个爱好有:'
}
function getHobby(a, b) {
console.log(this.text + a + '和' + b)
}
getHobby.call(obj, '足球', '羽毛球')
// 我的两个爱好有:足球和羽毛球
apply()的用法
var obj = {
text: '我的两个爱好有:'
}
function getHobby(a, b) {
console.log(this.text + a + '和' + b)
}
getHobby.apply(obj, ['足球', '羽毛球'])
// 我的两个爱好有:足球和羽毛球
bind()的用法
var obj = {
text: '我的两个爱好有:'
}
function getHobby(a, b) {
console.log(this.text + a + '和' + b)
}
getHobby.bind(obj, '足球', '羽毛球')()
// 我的两个爱好有:足球和羽毛球
1.三者的第一个参数,都是this的指向。
- call和bind在第一个参数后面传入的是一个参数列表。而 apply的第二个参数为参数数组。
- call和apply是立即调用。而bind返回对应函数,需要自己再调用()。
9. js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
clientHeight:表示的是可视区域的高度,不包含border和滚动条
offsetHeight:表示可视区域的高度,包含了border和滚动条
scrollHeight:表示了所有区域的高度,包含了因为滚动被隐藏的部分。
clientTop:表示边框border的厚度,在未指定的情况下一般为0
scrollTop:滚动后被隐藏的高度,获取对象相对于由offsetParent属性指定的父坐标(css定位的元素或body元素)距离顶端的高度。
10. 异步加载JS的五种方式
1. <script>标签的async="async"属性
HTML5中新增的属性,Chrome、FF、IE9&IE9+均支持(IE6~8不支持)。此外,这种方法不能保证脚本按顺序执行。
2. <script>标签的defer="defer"属性
兼容所有浏览器。此外,这种方法可以确保所有设置defer属性的脚本按顺序执行。
3. 动态创建<script>标签
(function(){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "http://code.jquery.com/jquery-1.7.2.min.js";
var tmp = document.getElementsByTagName('script')[0];
tmp.parentNode.insertBefore(script, tmp);
})();
11. js节流和防抖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<style>
html,
body {
height: 500%;
}
</style>
<body>
<button id='btn'>点</button>
<script>
//节流:一个函数执行后,只有大于设定的执行周期后,才会执行第二次,
//有个需要频繁出发的函数,出于性能优化,在规定的时间内,只让出发的第一次生效,后边的不生效
/* fn被节流的函数 deley 规定时间*/
function throttle(fn, delay) {
//记录上一次函数出发时间
let lastTime = 0
return function() {
//记录当前函数触法的时间
let nowTime = Date.now()
if (nowTime - lastTime > delay) {
fn.call(this)
lastTime = nowTime
}
}
}
document.onscroll = throttle(() => {
console.log('触发成功!' + Date.now())
}, 1000)
//------------------------------------------------------------------------------------------------------------------------
//防抖函数:一个频繁出发的函数,在规定的某个时间内,只让最后一次生效,前边的不生效如:频繁点击按钮
function debounce(fn, delay) {
let timer = null
return function() {
//清楚上一次延时器
clearTimeout(timer)
//重新设置新的延时器,
timer = setTimeout(() => {
fn.call(this)
}, delay)
}
}
document.getElementById('btn').onclick = debounce(() => {
console.log("触发了")
}, 2000)
</script>
</body>
</html>
12. js垃圾回收机制
解决内存的泄露,垃圾回收机制会定期(周期性)找出那些不再用到的内存(变量),然后释放其内存。
现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。
标记清除
js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"。
function test(){
var a = 10; //被标记"进入环境"
var b = "hello"; //被标记"进入环境"
}
test(); //执行完毕后之后,a和b又被标记"离开环境",被回收
垃圾回收机制在运行的时候会给存储再内存中的所有变量都加上标记(可以是任何标记方式),然后,它会去掉处在环境中的变量及被环境中的变量引用的变量标记(闭包)。而在此之后剩下的带有标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾回收机制到下一个周期运行时,将释放这些变量的内存,回收它们所占用的空间。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数
语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
上图中,左下角的两个值,没有任何引用,所以可以释放。
如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。
const arr = [1,2,3,4];
console.log("hello world");
上面的代码中,数组[1,2,3,4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它是会持续占用内存。
如果增加一行代码,解除arr对[1,2,3,4]引用,这块内存就可以被垃圾回收机制释放了。
let arr = [1,2,3,4];
console.log("hello world");
arr = null;
上面代码中,arr重置为null,就解除了对[1,2,3,4]的引用,引用次数变成了0,内存就可以释放出来了。
因此,并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值,一旦不再用到,你必须检查
是否还存在对它们的引用。如果6是的话,就必须手动解除引用
13. Commonjs、AMD和CMD
为什么模块很重要,因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
于是下面三个模块。
Commonjs:开始于服务器端的模块化,同步定义的模块化,每个模块都是一个单独的作用域,模块输出,modules.exports,模块加载require()引入模块。
AMD:中文名异步模块定义的意思
基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上面的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?
var math = require('math');
math.add(2, 3);
第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。
这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:
require(['math'], function (math) {
math.add(2, 3);
});
math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js。
CMD
Common Module Definition 规范和 AMD 很相似,尽量保持简单,并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。
define(function(require, exports, module) {
var $ = require('jquery');
var Spinning = require('./spinning');
exports.doSomething = ...
module.exports = ...
})
14. 浅拷贝与深拷贝
https://blog.csdn.net/jiang7701037/article/details/98738487
方法https://www.cnblogs.com/echolun/p/7889848.html
15. 将原生的ajax封装成promise
var myNewAjax=function(url){
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest();
xhr.open('get',url);
xhr.send(data);
xhr.onreadystatechange=function(){
if(xhr.status==200&&readyState==4){
var json=JSON.parse(xhr.responseText);
resolve(json)
}else if(xhr.readyState==4&&xhr.status!=200){
reject('error');
}
}
})
}
16.实现两列等高
https://blog.csdn.net/dizuncainiao/article/details/78191815
http://www.cssaaa.com/skill/163.html
17. 代码的执行顺序
https://www.cnblogs.com/wangziye/p/9566454.html
setTimeout(function(){console.log(1)},0);
new Promise(function(resolve,reject){
console.log(2);
resolve();
}).then(function(){console.log(3)
}).then(function(){console.log(4)});
process.nextTick(function(){console.log(5)});
console.log(6);
输出2.6..5.3.4.1
执行顺序
script(主程序代码)—>process.nextTick—>Promises...——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering
18.如何实现sleep的效果
1.while循环的方式
function sleep(ms){
var start=Date.now(),expire=start+ms;
while(Date.now()<expire);
console.log('1111');
return;
}
执行sleep(1000)之后,休眠了1000ms之后输出了1111。上述循环的方式缺点很明显,容易造成死循环。
2.通过promise来实现
function sleep(ms){
var temple=new Promise(
(resolve)=>{
console.log(111);setTimeout(resolve,ms)
});
return temple
}
sleep(500).then(function(){
//console.log(222)
})
//先输出了111,延迟500ms后输出222
3. 通过async封装
function sleep(ms){
return new Promise((resolve)=>setTimeout(resolve,ms));
}
async function test(){
var temple=await sleep(1000);
console.log(1111)
return temple
}
test();
//延迟1000ms输出了1111
4. 通过generate来实现
function* sleep(ms){
yield new Promise(function(resolve,reject){
console.log(111);
setTimeout(resolve,ms);
})
}
sleep(500).next().value.then(function(){console.log(2222)})
19. js判断数据类型
1.typeof
返回数据类型,包含这7种: number、boolean、symbol、string、object、undefined、function。
typeof null 返回类型错误,返回object
2. toString 这个是最完美的
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
3.constructor
constructor是原型prototype的一个属性,当函数被定义时候,js引擎会为函数添加原型prototype,并且这个prototype中constructor属性指向函数引用, 因此重写prototype会丢失原来的constructor。
4. instanceof
instanceof 是用来判断 A 是否为 B 的实例,表达式为:A instanceof B,如果 A 是 B 的实例,则返回 true,否则返回 false。 在这里需要特别注意的是:instanceof 检测的是原型,
instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
20. js数组常用方法
1. Array.map()
此方法是将数组中的每个元素调用一个提供的函数,结果作为一个新的数组返回,并没有改变原来的数组
let arr = [1, 2, 3, 4, 5]
let newArr = arr.map(x => x*2)
//arr= [1, 2, 3, 4, 5] 原数组保持不变
//newArr = [2, 4, 6, 8, 10] 返回新数组
2. Array.forEach()
此方法是将数组中的每个元素执行传进提供的函数,没有返回值,注意和map方法区分
let arr = [1, 2, 3, 4, 5]
num.forEach(x => x*2)
// arr = [1, 2, 3, 4, 5] 数组改变,注意和map区分
3.Array.filter()
此方法是将所有元素进行判断,将满足条件的元素作为一个新的数组返回
let arr = [1, 2, 3, 4, 5]
const isBigEnough = value => value >= 3
let newArr = arr.filter(isBigEnough )
//newNum = [3, 4, 5] 满足条件的元素返回为一个新的数组
4.Array.every()
此方法是将所有元素进行判断返回一个布尔值,如果所有元素都满足判断条件,则返回true,否则为false:
let arr = [1, 2, 3, 4, 5]
const isLessThan4 = value => value < 4
const isLessThan6 => value => value < 6
arr.every(isLessThan4 ) //false
arr.every(isLessThan6 ) //true
5. Array.some()
此方法是将所有元素进行判断返回一个布尔值,如果存在元素都满足判断条件,则返回true,若所有元素都不满足判断条件,则返回false:
let arr= [1, 2, 3, 4, 5]
const isLessThan4 = value => value < 4
const isLessThan6 = value => value > 6
arr.some(isLessThan4 ) //true
arr.some(isLessThan6 ) //false
6.Array.reduce()
此方法是所有元素调用返回函数,返回值为最后结果,传入的值必须是函数类型:
let arr = [1, 2, 3, 4, 5]
const add = (a, b) => a + b
let sum = arr.reduce(add)
//sum = 15 相当于累加的效果
与之相对应的还有一个 Array.reduceRight() 方法,区别是这个是从右向左操作的
7. Array.push()
此方法是在数组的后面添加新加元素,此方法改变了数组的长度:
8. Array.pop()
此方法在数组后面删除最后一个元素,并返回数组,此方法改变了数组的长度:
let arr = [1, 2, 3, 4, 5]
arr.pop()
console.log(arr) //[1, 2, 3, 4]
console.log(arr.length) //4
9. Array.shift()
此方法在数组后面删除第一个元素,并返回数组,此方法改变了数组的长度:
let arr = [1, 2, 3, 4, 5]
arr.shift()
console.log(arr) //[2, 3, 4, 5]
console.log(arr.length) //4
10.Array.unshift()
此方法是将一个或多个元素添加到数组的开头,并返回新数组的长度:
let arr = [1, 2, 3, 4, 5]
arr.unshift(6, 7)
console.log(arr) //[6, 7, 1, 2, 3, 4, 5]
console.log(arr.length) //7
11.Array.isArray()
判断一个对象是不是数组,返回的是布尔值
12.Array.concat()
此方法是一个可以将多个数组拼接成一个数组:
let arr1 = [1, 2, 3]
arr2 = [4, 5]
let arr = arr1.concat(arr2)
console.log(arr)//[1, 2, 3, 4, 5]
13. Array.toString()
此方法将数组转化为字符串:
let arr = [1, 2, 3, 4, 5];
let str = arr.toString()
console.log(str)// 1,2,3,4,5
14. Array.splice(开始位置, 删除的个数,元素)
万能方法,可以实现增删改:
let arr = [1, 2, 3, 4, 5];
let arr1 = arr.splice(2, 0 'haha')
let arr2 = arr.splice(2, 3)
let arr1 = arr.splice(2, 1 'haha')
console.log(arr1) //[1, 2, 'haha', 3, 4, 5]新增一个元素
console.log(arr2) //[1, 2] 删除三个元素
console.log(arr3) //[1, 2, 'haha', 4, 5] 替换一个元素
15.Array. sort()
如果想按照其他标准进行排序,就需要提供比较函数,该函数要比较两个值,然后返回一个用于说明这两个值的相对顺序的数字。比较函数应该具有两个参数 a 和 b,其返回值如下:
若 a 小于 b,在排序后的数组中 a 应该出现在 b 之前,则返回一个小于 0 的值。
若 a 等于 b,则返回 0。
若 a 大于 b,则返回一个大于 0 的值。
function sortNumber(a,b)
{
return a - b
}
var arr = new Array(6)
arr[0] = "10"
arr[1] = "5"
arr[2] = "40"
arr[3] = "25"
arr[4] = "1000"
arr[5] = "1"
document.write(arr.sort(sortNumber))
16.Array.reverse()
reverse() 方法用于颠倒数组中元素的顺序。
21.数组去重
1. 利用ES6 Set去重
Set :可以接收一个数组或者是类数组对象,自动去重。
Array.from:把类数组对象、可迭代对象转化为数组。
2018062011354884.png
2. 利用for嵌套for,然后splice去重
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
3. 利用indexOf
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
var arr = [1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 7];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) { //从头到尾检索newArr,检查它里面是否含有arr[i]
newArr.push(arr[i]);//如果没有arr[i],则将arr[i]插入到newArr中去
}
}
4.利用sort()
利用sort()排序方法,然后根据排序后的结果进行遍历及相邻元素比对。
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return;
}
arr = arr.sort()
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
5. 利用includes
includes() 方法用于判断字符串是否包含指定的子字符串。
如果找到匹配的字符串则返回 true,否则返回 false。
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
6. 利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
7.利用递归去重
function unique(arr) {
var array= arr;
var len = array.length;
array.sort(function(a,b){ //排序后更加方便去重
return a - b;
})
function loop(index){
if(index >= 1){
if(array[index] === array[index-1]){
array.splice(index,1);
}
loop(index - 1); //递归loop,然后数组去重
}
}
loop(len-1);
return array;
}
22. 性能优化
https://zhuanlan.zhihu.com/p/113864878?from_voters_page=true
https://blog.csdn.net/liujie19901217/article/details/52074018
23. JS的语言特性
运行在客户端浏览器上;
不用预编译,直接解析执行代码;
是弱类型语言,较为灵活;
与操作系统无关,跨平台的语言;
脚本语言、解释性语言
24.判断变量是数组Array类型
https://blog.csdn.net/lee_magnum/article/details/11555981
25. 函数柯里化
function curry(fn,val){
return function(){
//转化为数组
var args = Array.from(arguments)
if(val){
//参数拼接
args = args.concat(val)
}
//fn.length 表示函数需要传入多少参数
//递归调用 当参数相等停止递归
if(fn.length > args.length){
return curry(fn,args)
}
return fn.apply(null,args)
}
}
function sum(a, b, c) {
console.log(a + b + c);
}
const fn = curry(sum);
fn(1,2,3)
fn(1)(2)(3)
26. 箭头函数 可以new吗
27. TS函数重载
//函数的名字相同,函数的参数和个数不同
//需求:有一个add函数,他可以接受2个sting类型的参数进行拼接,也可以接受2个number类型的参数进行相加
//函数重载声明
function add(x:string,y:string) :string
function add(x:number,y:number) :number
function add(x:string|number,y:string|number) :string|number{
if(typeof x ==='string' && typeof y ==='string'){
return x+y //字符串拼接
}
else if(typeof x ==='number' && typeof y ==='number'){
return x+y //数字相加
}
}
console.log(add(1,2));
console.log(add('11','22'));
// console.log(add('11',12)); 报错
28. js函数重载
29. fetch
fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
30. RESTful
31. echarts底层原理
1、ECharts 是一个轻量级的 javascript 图形库,纯 js 实现,MVC 封装,数据驱动。
2、Svg 和 Canvas 是两个可以选择的类库之一,其中 svg 交互性更好,性能较弱,不适用于移动端,在绘制数万个点时会崩溃。而 canvas 的渲染速度和性能更好,echarts 在 canvas 上构建一层 MVC层,使得它可以像 svg 一样交互。
3、ECharts 的特点:重要性和优先级依次递减,设计效果直观、生动,能够交互,可个性化定制。
4、ECharts 总体结构是基于 MVC 架构的,各部分的主要作用是:
Storage(M):模型层,实现图形数据的CURD(增删改查)管理;
Painter(V): 视图层,实现canvas 元素的生命周期管理,即:视图渲染、更新控制、绘图;
Handler(C):控制层,事件交互处理,实现完整的dom事件模拟封装。
Storage 是一个类似数据的仓库,提供各种数据的读、写、改、删等操作。Painter 持有 Storage 对象,即:Painter 读取 Storage 进行绘图。Handler持有了Storage 对象和 Painter 对象,控制层对模型层有CURD关系,即:Handler 通过访问 Storage 对象提供的数据增删改查操作,实现事件交互处理所需的数据部分;控制层与视图层存在 call 关系,即:Handler 通过访问 Painter 对象提供的视图操作,实现事件交互处理的视图部分。
32. WebSocket
33. a = a.x = {x:1}如何理解
https://blog.csdn.net/qiphon3650/article/details/78860973
34. javaScript和java有什么区别
- 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
- 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
- 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须做声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。
- 代码格式不一样。
- JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun Microsystems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是Oak语言。
35. TS和JS有什么区别
- ts是静态类语言,可以做到声明即文档,js是动态类语言相对更灵活。
- 如用ts写一个button组件可以清晰的知道,ButtonProps如是否必传. 可选,style是什么类型,disabled是什么类型,较js,ts更易于维护和拓展,可以做到代码即注释,避免一个月不见3,代码自己都忘记自己写了什么的尴尬,
- ts对比js基础类型上,增加了 void/never/any/元组/枚举/以及一些高级类型
- js没有重载概念,ts有可以重载
- vscode/ide对ts有很友好的提示
- ts更利于重构
36. 立即执行函数
37. Symbol
38. 手写compose
function compose() {
var args = arguments;
var start = args.length - 1;
return function () {
var i = start - 1;
var result = args[start].apply(this, arguments);
while (i >= 0){
result = args[i].call(this, result);
i--;
}
return result;
};
}
------------------------------------------
function addHello(str){
return 'hello '+str;
}
function toUpperCase(str) {
return str.toUpperCase();
}
function reverse(str){
return str.split('').reverse().join('');
}
var composeFn=compose(reverse,toUpperCase,addHello);
console.log(composeFn('ttsy')); // YSTT OLLEH
网友评论