javascript综合 四(es6扩展运算符,script标签的defer和async,基本数据类型和引用数据类型,浅拷贝和深拷贝,decodeURLComponent()函数,==和===的区别)

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


特殊:NaNNaN 永远不相等


console.log(NaN == NaN); // NaN和NaN 永远不相等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值