javascript知识简略
文章目录
一、闭包
1、闭包:MDN的解释:闭包是函数和声明该函数的词法环境的组合。
说白了就是函数嵌套函数,内部函数能够访问外部函数的变量
(function() { var a = 1; function add() { var b = 2 var sum = b + a console.log(sum);
// 3 }
add() })()
2、作用
- 闭包最大的作用就是隐藏变量,闭包的一大特性就是内部函数总是可以访问其所在的外部函数
中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后- 基于此特性,JavaScript可以实现私有变量、特权变量、储存变量等
- 我们就以私有变量举例,私有变量的实现方法很多,有靠约定的(变量名前加_),有靠Proxy
代理的,也有靠Symbol这种新数据类型的。
3、优点
- (1)、可以隔离作用域,不造成全局污染
- (2)、长期驻扎在内存中
- (3)、私有成员的存在
4、缺点
- 由于闭包长期驻留内存,则长期这样会导致内存泄露
- 如何解决内存泄露:将暴露全外部的闭包变量置为null
- 内存的使用量大
5、适用场景:封装组件,for循环和定时器结合使用,for循环和dom事件结合.可以在性能优化的过程
中,节流防抖函数的使用,导航栏获取下标的使用
实例:
function outer(){
var val = 0;
return function (){
val += 1; document.write(val + "<br />");
};
}
var outObj = outer();
outObj();//1,执行val += 1后,val还在
outObj();//2
outObj = null;//val 被回收
var outObj1 = outer();
outObj1();//1
outObj1();//2
二、原型链
原型链是理解JS面向对象很重要的一点,这里主要涉及到两个点,一是_ proto ,二是prototype;
具体流程: 首先在p1对象实例上查找是否有有play方法,如果有则调用执行,如果没有则用p1.proto(proto是一
个指向的作用,指向上一层的原型)往创建p1的类的原型上查找,也就是说往Person.prototype上查找,
如果在Person.prototype找到play方法则执行,否则继续往上查找,则用Person.prototye.proto继续
往上查找,找到Object.prototype,如果Object.prototype有play方法则执行之,否则用
Object.prototype.proto继续再往上查找,但Object.prototpye.proto上一级是null,也就是原型链的顶
级,结束原型链的查找
三、js原生事件的绑定
JS原生绑定事件主要为三种:
一是html事件处理程序
二是DOM0级事件处理程序
三是DOM2级事件处理程序
其中:html事件现在早已不用了,就是在html各种标签上直接添加事件,类似于css的行内样
式,缺点是不好维护,因为散落在标签中,也就是耦合度太高
四、js原生dom操作
1、查找:
getElementByid,
getElementsByTagName,
querySelector,
querySelectorAll
2、插入:
appendChild,insertBefore
3、删除:
removeChild
4、克隆:
cloneNode
5、设置和获取属性:
setAttribute(“属性名”,”值”)
getAttibute(“属性名”)
五、es6新增特性
- 新增了块级作用域(let,const)
- 提供了定义类的语法糖(class)
- 新增了一种基本数据类型(Symbol)
- 新增了变量的解构赋值
- 函数参数允许设置默认值,引入了rest参数,新增了箭头函数
- 数组新增了一些API,如 isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等
方法 - 对象和数组新增了扩展运算符
- ES6 新增了模块化(import/export)
- ES6 新增了 Set 和 Map 数据结构
- ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
- ES6 新增了生成器(Generator)和遍历器(Iterator)
六、js数组内置遍历方法
JS数组内置遍历(遍历就是循环的意思)方法主要有:
- forEach
这个方法是为了取代for循环遍历数组的,返回值为undefined例如:
let arrInfo=[4,6,6,8,5,7,87] arrInfo.forEach((item,index,arr)=>{ //遍历逻辑 })
其中:
- item代码遍历的每一项,
- index:代表遍历的每项的索引,
- arr代表数组本身
- filter
是一个过滤遍历的方法,如果返回条件为true,则返回满足条件为true的新数组
let arrInfo=[4,16,6,8,45,7,87] •
let resultArr=arrInfo.filter((item,index,arr)=>{
//例如返回数组每项值大于9的数组 return item>9 })
- map
这个map方法主要对数组的复杂逻辑处理时用的多,特别是react中遍历数据,也经常用到,
写法和forEach类似 - some
这个some方法用于只要数组中至少存在一个满足条件的结果,返回值就为true,否则返回
fasel, 写法和forEach类似 - every
这个every方法用于数组中每一项都得满足条件时,才返回true,否则返回false, 写法和
forEach类似
七、js作用域
- JS作用域也就是JS识别变量的范围,作用域链也就是JS查找变量的顺序
- 先说作用域,JS作用域主要包括全局作用域、局部作用域和ES6的块级作用域
- 全局作用域:也就是定义在window下的变量范围,在任何地方都可以访问,
- 局部作用域:是只在函数内部定义的变量范围
- 块级作用域:简单来说用let和const在任意的代码块中定义的变量都认为是块级作用域中的变
量,例如在for循环中用let定义的变量,在if语句中用let定义的变量等等
注:
- 尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对bug查找不利。
- 而所谓的作用域链就是由最内部的作用域往最外部,查找变量的过程.形成的链条就是作用域链
八、事件代理及其原理
JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,然后当点击
子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,主要目的是为了提升性能,因
为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了,在原生js里面是通过event对象的
targe属性实现
var ul = document.querySelector("ul");
ul.onclick = function(e){//e指event,事件对象
var target = e.target || e.srcElement;
//target获取触发事件的目标(li) if(target.nodeName.toLowerCase() == 'li'){
//目标(li)节点名转小写字母,
不转的话是大写 字母• alert(target.innerHTML) } }
jq方式实现相对而言简单 $(“ul”).on(“click”,“li”,function(){//事件逻辑}) 其中第二个参数指的是触发事件的
具体目标,特别是给动态添加的元素绑定事件
九、call、apply、bind的区别
call,apply,bind主要作用都是改变this指向的,但使用上略有区别,说一下区别
- call和apply的主要区别是在传递参数上不同,call后面传递的参数是以逗号的形式分开的,apply
传递的参数是数组形式 [Apply是以A开头的,所以应该是跟Array(数组)形式的参数] - bind返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如:bind(obj,参数1,参数2)(),bind只能以逗号分隔形式,不能是数组形式
十、es6模板与commonjs模块有何区别
- ES6 Module和CommonJS模块的区别:- CommonJS是对模块的浅拷贝,ES6 Module是对模块的引用,即ES6 Module只存只读,不能改变其值,具体点就是指针指向不能变,类似const
- import的接口是 read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但
可以改变变量内部指针指向,可以对commonJS对重新赋值(改变指针指向),但是对ES6 Module
赋值会编译报错。
ES6 Module和CommonJS模块的共同点:
- CommonJS和ES6 Module都可以对引入的对象进行赋值,即对对象内部属性的值进行改变。
十一、await/async是什么
async 函数,就是 Generator 函数的语法糖,它建立在Promises上,并且与所有现有的基于Promise的
API兼容。
- Async—声明一个异步函数(async function someName(){…})
- 自动将常规函数转换成Promise,返回值也是一个Promise对象
- 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
- 异步函数内部可以使用await
- Await—暂停异步的功能执行(var result = await someAsyncCall()😉
- 放置在Promise调用之前,await强制其他代码等待,直到Promise完成并返回结果
- 只能与Promise一起使用,不适用与回调
- 只能在async函数内部使用
十二、async/await相比于promise的优势
- 代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调用也会带来额外的阅读负
担- Promise传递中间值非常麻烦,而async/await几乎是同步的写法,非常优雅
- 错误处理友好,async/await可以用成熟的try/catch,Promise的错误捕获非常冗余
- 调试友好,Promise的调试很差,由于没有代码块,你不能在一个返回表达式的箭头函数中设置断
点,如果你在一个.then代码块中使用调试器的步进(step-over)功能,调试器并不会进入后续
的.then代码块,因为调试器只能跟踪同步代码的『每一步』。
十三、同步与异步的区别
- 同步:
- 浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出
现,用户看到新内容,进行下一步操作- 代码从上往下依次执行,执行完当前代码,才能执行下面的代码。(阻塞)
- 异步:
- 浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内
容也会出现,用户看到新内容- 代码从上往下依次执行,没执行完当前代码,也能执行下面的代码。(非阻塞)
十四、深拷贝与浅拷贝
1、区别
- 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。浅拷贝只复制
对象的第一层属性- 但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
对对象的属性进行递归复制
2、实现方式
浅拷贝:
- 使用Object.assign({},obj)第一个参数是一个空对象,第二个参数是你要复制的对象;通过这
个方法我们知道浅拷贝不能修改基础的数据类型,可以修改引用的数据类型;- ES6中的…扩展运算符来进行浅拷贝的实现;
- Object.assign()实现
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目 标对象。
但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本 身。
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100; console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2);
// { a: 10, b: 100, c: 30 }
深拷贝:
- 对象只有一层的话可以使用上面的:Object.assign()函数
- 转成 JSON 再转回来
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } <-- 沒被改到 console.log(obj2);
// { body: { a: 20 } } console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false
用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。
- 使用Object.create()方法
直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。
function deepClone(initalObj, finalObj) {
var obj = finalObj || {};
for (var i in initalObj) {
var prop = initalObj[i];
// 避免相互引用对象导致死循环,如 initalObj.a = initalObj的情况
if(prop === obj) {
continue;
}
if (typeof prop === 'object') {
obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
obj[i] = prop;
}
}
return obj;
}
十五、json与jsonp的区别
- json返回的是一串json格式数据;而jsonp返回的是脚本代码(包含一个函数调用)
- jsonp的全名叫做json with padding,就是把json对象用符合js语法的形式包裹起来以使其他的网站可以
请求到,也就是将json封装成js文件传过去。
十六、如何优化代码
- 代码重用
- 避免全局变量(命名空间,封闭空间,模块化 mvc…)
- 拆分函数避免函数过于臃肿