es6扩展运算符
es6扩展运算符
扩展运算符可以将数组/对象/字符串展开
- 立即执行
保证先后顺序,html解析器遇到时将停止解析(阻塞),待脚本下载并执行后,继续解析这之后的文档
- 推迟执行
保证先后顺序,html解析器遇到时不阻塞,即脚本将被异步下载,一边下载脚本一边解析html文档,文档解析完成后执行脚本。在DOMContentload事件执行前执行
- 尽快执行
不保证先后顺序,html解析器遇到时不阻塞,即脚本将被异步下载,一遍下载脚本一遍解析html文档,当脚本下载完成时立即执行(这时候会阻塞),执行完后继续解析之后的文档
其中蓝色代表js脚本网络加载时间,红色代表js脚本执行时间,绿色代表html解析。
clientWidth,offersetWidth,scrollHeight等
client不包括边框和滚动条
行内元素的clientWidth为0
clientWidth=width+padding-垂直滚动条宽度
clientHeight=height+padding-水平滚动条宽度
clientTop=border.top(上边框的宽度)
clientLeft=border.left(左边框的宽度)
offset包含边框和滚动条。有无滚动条都不影响
offsetWidth=width(已包含scrollbar)+padding+border
offsetWidth=width(已包含scrollbar)+padding+border
offsetTop=当前元素上边框外边缘到最近的已定位父级(offsetparent) 上边框 内边缘的 距离。如果父级都没有定位,则分别是到body 顶部 和左边的距离
offsetLeft=当前元素左边框外边缘 最近的已定位父级(offsetParent) 左边框内边缘的距离。如果父级都没有定位,则分别是到body 顶部和左边的距离
scroll包含content超出部分的宽度,包含padding但不包含scrollbar
scrollWidth=可视区域宽度+被隐藏区域宽度
scrollHeight=可视区域高度+被隐藏区域高度
scrollTop=内容层顶部 到 可视区域顶部的距离,即在出现了纵向滚动条的情况下,滚动条拉动的距离。
scrollLeft=内容层左端 到 可视区域左端的距离,即在出现了横向滚动条的情况下,滚动条拉动的距离。
基本数据类型和引用数据类型
基本数据类型:Number,String,Boolean,Undefined, Null
引用数据类型:Object(Array,Date,RegExp,Function)
基本数据类型保存在栈中
引用数据类型保存在堆中,在栈中保存了一个对堆内存中实际对象的引用,即数据在堆内存中的地址
- 按引用访问:js不允许直接访问保存在堆内存中的对象,所以在访问一个对象时,首先得到的是这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值;
- 基本数据类型使用typeof可以返回其基本数据类型,但是NULL类型会返回object,因此null值表示一个空对象指针;引用数据类型使用typeof会返回object,此时需要使用instanceof来检测引用数据类型;
浅拷贝和深拷贝
浅拷贝和深拷贝都只针对于引用数据类型,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象;
区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制;
实现浅拷贝的方法
- for…in…实现浅拷贝
// 只复制第一层的浅拷贝
function simpleCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
for (let i in obj1) {
obj2[i] = obj1[i];
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
console.log(obj2);
console.log(obj1);
obj2.a = 3;
obj2.c.d = 4;
//obj2拷贝了obj1的第一层属性,对于c属性,因为c是一个引用数据类型,所以obj2拷贝的是地址,obj2对c的改变会影响到obj1
//深拷贝和浅拷贝对基本数据类型没有影响
console.log(obj1.a); // 1 因为a是一个基本数据类型,所以obj2里面的a改变不会影响obj1里面的a
console.log(obj2.a); // 3
console.log(obj1.c.d); // 4 因为c是一个引用数据类型,所以obj2里面的c改变不会影响obj1里面的
console.log(obj2.c.d); // 4
- Object.assign()实现浅拷贝
let obj1 = {
a: {
b: 1
},
c: 2
}
let obj2 = Object.assign({},obj1)
obj2.a.b = 3;
obj2.c = 3
console.log(obj1.a.b); // 3 a是一个引用数据类型 所以obj2改变obj1也会改变
console.log(obj2.a.b); // 3
console.log(obj1.c); // 2 c是一个普通数据类型 所以obj1和obj2的改变互不影响
console.log(obj2.c); // 3
- Object.create()实现浅拷贝
// Object实现拷贝2,浅拷贝
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = Object.create(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
实现深拷贝的方法
- 手动实现深拷贝??
( 这个实现的应该不是深拷贝)
let obj1 = {
a: 1,
b: 2
}
let obj2 = {
a: obj1.a,
b: obj1.b
}
obj2.a = 3;
alert(obj1.a); // 1 对基本数据类型没有影响
alert(obj2.a); // 3
let obj1 = {
a: {
b: 2
}
}
let obj2 = {
a: obj1.a
}
obj2.a.b = 3;
console.log(obj1.a.b); // 3 a是引用数据类型
console.log(obj2.a.b); // 3
- 递归实现深拷贝
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
if (obj1.hasOwnProperty(i)) {
// 如果子属性为引用数据类型,递归复制
if (obj1[i] && typeof obj1[i] === "object") {
obj2[i] = deepCopy(obj1[i]);//这里是obj2[i]=deepCopy(obj1[i])!!!不只是deepCopy(obj1[i])!!!
} else {
// 如果是基本数据类型,只是简单的复制
obj2[i] = obj1[i];
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
console.log(obj1.a); // 1 深度克隆 不影响基本数据类型,即两个对象各自的基本数据类型不会因对方改变而改变
console.log(obj2.a); // 3
console.log(obj1.c.d); // 3 引用数据类型也不会受影响
console.log(obj2.c.d); // 4
缺陷:当遇到两个互相引用的对象,会出现死循环的情况,为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环;
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子属性为引用数据类型,递归复制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : {};
//arguments.callee是一个指向正在执行的函数的指针,可以用来实现对函数的递归调用
arguments.callee(prop, obj2[i]); // 递归调用
} else {
// 如果是基本数据类型,只是简单的复制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
- Object.create()实现深拷贝
// Object.create实现深拷贝1,但也只能拷贝一层
function deepCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
if (obj1 && typeof obj1 === "object") {
for (var i in obj1) {
var prop = obj1[i]; // 避免相互引用造成死循环,如obj1.a=obj
if (prop == obj1) {
continue;
}
if (obj1.hasOwnProperty(i)) {
// 如果子属性为引用数据类型,递归复制
if (prop && typeof prop === "object") {
obj2[i] = (prop.constructor === Array) ? [] : Object.create(prop);
} else {
// 如果是基本数据类型,只是简单的复制
obj2[i] = prop;
}
}
}
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = deepCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 3
alert(obj2.c.d); // 4
- 使用JSON.stringify和JSON.parse实现深拷贝:JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象;
function deepCopy(obj1){
let _obj = JSON.stringify(obj1);
let obj2 = JSON.parse(_obj);
return obj2;
}
var a = [1, [1, 2], 3, 4];
var b = deepCopy(a);
b[1][0] = 2;
alert(a); // 1,1,2,3,4
alert(b); // 1,2,2,3,4
缺陷:它会抛弃对象的constructor,深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object;这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,也就是说,只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON;
let obj1 = {
fun:function(){
alert(123);
}
}
let obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun); // function
console.log(typeof obj2.fun); // undefined
- 函数库lodash,也有提供_.cloneDeep用来做深拷贝;
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false
JavaScript decodeURLComponent()函数
decodeURIComponent(URIstring)
URIstring 必需。一个字符串,含有编码 URI 组件或其他要解码的文本。
decodeURIComponent() 函数可对 encodeURIComponent() 函数编码的 URI 进行解码。
<script type="text/javascript">
var test1="http://www.w3school.com.cn/My first/"
document.write(encodeURIComponent(test1)+ "<br />")
document.write(decodeURIComponent(test1))
//http%3A%2F%2Fwww.w3school.com.cn%2FMy%20first%2F
//http://www.w3school.com.cn/My first/
</script>
和=的区别
==和===的区别
== 在比较时等号左右两侧数据类型不同时会先转成相同数据类型,再比较
=== 比较类型和值
== 是相对比较; === 是绝对比较
1.1 字符串 == 数字 ;字符串转换成数字
console.log(1 == '1'); // true
1.2 布尔值 == 数字; 布尔值转成数字
console.log(1 == true); // true;
1.3 布尔值 == 字符串; 布尔值转数字,字符串也转成数字,然后进行比较;
console.log(false == '0'); // true
1.4
console.log(null == 1); //false
console.log(null == '1'); //false
console.log(undefined == '22'); //false
console.log(true == undefined); //false
console.log(true == null); //false
console.log(undefined == null); //true
// null 和undefined和其他数据类型比较都是false
// 但是undefined == null为true
1.5 对象 == 对象; 比较的是空间地址,地址相同返回true
console.log({} == {}); // false
1.6 对象 == 字符串; 对象转成字符串,然后和字符串比较
console.log({} == '[object Object]'); // true
console.log([1, 2, 3] == "1,2,3");//true
1.7 对象 == 布尔值;对象先转成字符串,再转数字,布尔值也转成数字,在比较这两个数字
console.log({} == true); // false
console.log({} == false); // false
console.log([] == false); // true
console.log([] == true); // false
1.8 对象 == 数字;对象先转成字符串,然后再转成数字
console.log({} == 1); // false
console.log({} == 0); // false
console.log([] == 1); // false
console.log([] == 0); // true
特殊:NaN和NaN 永远不相等
console.log(NaN == NaN); // NaN和NaN 永远不相等