03es6第一天
ES6第一天
ECMA (European Computer Manufacturers Association)中文名称为欧洲计算机制造商协抖这个组织的目标是评估、开发和认可电信和计算机标准。1994年后该组织改名为Ecma国际。
ECMAScript是由Ecma国际通过ECMA-262标准化的脚本程序设计语言。
TC39 (Technical Committee 39)是推进 ECMAScript发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39定期召开会议,会议由会员公司的代表与特邀专家出席
一、关键字
1.1 let 关键字(es6)
用来定义块作用域变量的
var 定义函数级作用域变量的:
在函数内部定义的变量,外部无法访问,
在代码块(if,for等)中定义的变量,外部可以访问
let 定义块作用域变量的:
在函数内部定义的变量,外部无法访问,
在代码块(if,for等)中定义的变量,外部仍然无法访问
let 与 var 比较
-
作用域
var 函数级作用域
let 块级作用域
-
重复定义
var 可以重复定义变量
let不可以重复定义变量
-
声明前置
var 支持声明前置
let不支持声明前置
-
for循环中存储数据
var 不能存储数据
let 可以存储数据
-
被 window 挂载
var可以被挂载,可以被window所访问
let不能被挂载,不能被window所访问
<!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>
<body>
<script>
// 作用域:
// var 函数级作用域
// let 块级作用域
// function demo() {
// var a = 10;
// let b = 20;
// }
// demo();
// console.log(a, b);
// 在if代码块中
// if (true) {
// var a = 10;
// let b = 20;
// }
// console.log(a);
// console.log(b);
// 在for循环中:
// for (var i = 0; i < 5; i++) {
// for ( let i = 0; i < 5; i++) {
// }
// console.log(i);
// 重复定义:
// var 可以重复定义变量
// var num = 100;
// var num = 200;
// let不可以重复定义变量
// let num = 100;
// let num = 200;
// console.log(num);
// 声明前置
// var 支持声明前置
// console.log(num);
// var num = 100;
// let不支持声明前置
// let num = 200;
// for循环中存储数据
// var 不能存储数据
// 定义数组
// var arr = [];
// for (var i = 0; i < 5; i++) {
// // 存储函数
// // arr[i] = function() {
// // console.log(i);
// // }
// // 可以利用闭包解决
// arr[i] = (function(a) {
// // 返回内部函数
// return function() {
// console.log(a);
// }
// })(i)
// }
// let 可以存储数据
// for (let i = 0; i < 5; i++) {
// // 存储函数
// arr[i] = function() {
// console.log(i);
// }
// }
// arr[0]();
// arr[1]();
// arr[2]();
// 被window挂载
// var可以被挂载
var msg = 'hello';
// let不能被挂载
let num = 500;
console.log(window.msg);
console.log(window.num);
</script>
</body>
</html>
1.2 const 关键字(es3)
const关键字是用于定义常量(一旦定义无法改变的变量,通常是表示一些固定不变的数据)
使用const关键字的特点:
1 无法被修改 只是const所修饰的不能修改而已,如果是对象,对象里的属性还可以修改
2 支持块作用域
3 无法重复定义
4 无法声明前置
5 不能被window挂载
6 不能作为for循环体中的变量使用,,原因就是1 它的值无法被修改,因此for循环并不可以用
7 值只能是值类型,如果是引用类型则可以被修改
工作中,通常是将用大写字母表示,并且横线分割单词,常用于定义配置量
在ES5中,我们可以通过冻结对象技术,或者设置writable:false特性,来模拟静态变量。
但问题是:值如果是引用类型依然会被修改
在ES3.1中,我们可以通过单例模式来模拟静态变量。在方法中只定义取值方法,而不定义赋值方法即可
【普及静态变量】
- 静态变量应该在静态区分配,不在堆上。每个变量定义的时候只能初始化一次,“初始化”从字面意思理解就是第一次,第二次赋值就不能算了。
- 静态变量的关键点在于不可以修改数据
- 就像上面说的,静态变量的生存期为整个程序,所以只能初始化一次。但是普通局部变量定义一次初始化后当函数结束调用变量就不存在了,再次调用的时候又分配一个变量又可以进行一次初始化。
- 栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
<!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>
<body>
<script>
// const关键字是用于定义常量(一旦定义无法改变的变量,通常是表示一些固定不变的数据)
// let虽然无法重复定义 但是可以被修改
// let num = 100;
// num = 200;
// 1 无法被修改
// const num = 100;
// num = 200;
// console.log(num);
// 2 支持块作用域
// if (true) {
// const a = 10;
// }
// console.log(a);
// 3 无法重复定义
// const num = 100;
// const num = 100;
// 4 无法声明前置
// console.log(num);
// const num = 100;
// 5 不能被window挂载
// const num = 100;
// console.log(window.num);
// 6 不能作为for循环体中的变量使用
// for (const i = 0; i < 5; i++) {
// console.log(i);
// }
// 7 值只能是值类型,如果是引用类型则可以被修改
// const obj = {
// a: 1,
// b: 2
// }
// obj.a = 100;
// obj.b = 200;
// console.log(obj);
</script>
<script>
// 在ES5中,我们可以通过冻结对象技术 模拟静态变量
// let obj = {
// msg: 'hello',
// num: 500
// }
// 冻结
// Object.freeze(obj);
// 通过设置特性的方式
// Object.defineProperties(obj, {
// msg: {
// // 不可修改
// writable: false
// },
// num: {
// writable: false
// }
// })
// 尝试操作对象
// 增
// obj.title = 'nihao';
// // 删
// delete obj.msg;
// // 改
// obj.num = 1000;
// console.log(obj);
// 在ES3.1版本中模拟静态变量
// 我们可以通过单例模式来模拟静态变量。在方法中只定义取值方法,而不定义赋值方法即可
let Conf = (function() {
let obj = {
msg: 'hello',
num: 500,
// 定义引用类型
// 不定义赋值方法即可
size: {
width: 100,
height: 200
}
}
// 取值方法
return function(key) {
return obj[key];
}
})()
console.log(Conf('msg'));
console.log(Conf('num'));
// 修改数据
Conf('size').width = 1000;
Conf('size').height = 2000;
console.log(Conf('size'));
</script>
</body>
</html>
二、字符串拓展
2.1 多行字符串
单行字符串:由一组单引号或者双引号定义的字符串
单行字符串的问题:
、 1 单行字符串不能换行
2 一些特殊的按键要使用转义字符 \n
3 一些特殊的字符要使用转义字符 \x20
4 字符串标志符号不能直接嵌套
单引号中不能直接写单引号,要转义 \’
双引号中不能直接写双引号,要转义 \”
......
ES6为了解决单行字符串中的问题,提供了多行字符串,
通过 飘点定义,在多行字符串中,只有 ` 需要转义 ,其他的字符,都可以直接书写
并且ES6多行字符串支持插值语法 :${key}
${}提供了js环境,因此我们可以写js表达式
ES6的插值语法,让其它框架的插值语法的重要性,大打折扣。
<!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>
<body>
<ul id="list">
</ul>
<script>
// 单行字符串问题:
// 1 单行字符串不能换行
// 2 一些特殊的按键要使用转义字符 \n
// 3 一些特殊的字符要使用转义字符 \012
// 4 字符串标志符号不能直接嵌套
// 单引号中不能直接写单引号,要转义 \’ 双引号中不能直接写双引号,要转义 \” ......
// let str = 'he\'ll"o \nwor\012ld';
// console.log(str);
// 为了解决以上的问题 Es6提供了多行字符串的语法
// 通过 ` 定义,在多行字符串中,只有`需要转义 \`,其他的字符,都可以直接书写
// let str = `
// hello
// world
// ''
// ""
// \`
// `
// console.log(str);
// 定义数据
// let size = {
// width: 100,
// height: 200
// }
// // 求出面积:
// // 单行串:
// // let result = '面积是:' + size.width + ' * ' + size.height + ' = ' + size.width * size.height;
// // 使用多行串
// let result = `面积是: ${size.width} * ${size.height} = ${size.width * size.height}`;
// console.log(result);
</script>
<script>
// 常量通常用大写字母表示:
const DATA = [
{
title: '新游|', text: '赛马娘火爆引发连锁效应 Humankind延期发售'
},
{
title: '手游|', text: '尼尔手游愚人节预告 英雄联盟手游国服消息首曝'
},
{
title: '趣闻|', text: 'DOTA龙之血评分9.5 拳头宣布将推出恋爱模拟游戏'
},
{
title: '专区|', text: 'Major淘汰赛败者组 炉石老友齐聚黄金公开赛'
}
]
// 定义一个变量
let html = '';
// 使用多行字符串处理模板
DATA.forEach(function(item, index) {
// 拼接模板
html += `
<li>
<strong>${item.title}</strong>
<span>${item.text}</span>
</li>
`
})
// 上树
document.getElementById('list').innerHTML = html;
</script>
</body>
</html>
2.2 原始字符串
在使用了转义字符之后,并且在浏览器查看的时候,我们只能看到结果,不能看到原始完整的字符串(包含转义字符),于是ES6中拓展了
String.raw方法用于,查看完整的原始字符串
使用方式:
String.raw``
参数通过多行字符串的形式传递,字符串中的转义字符串不会被转义
// 定义字符串
let str = 'hello \nworld';
console.log(str);
// ES6中拓展了String.raw方法,用于查看完整的原始字符串
// 飘点中间不能写str
console.log(String.raw`hello \nworld`);
2.3 重复字符串
ES6中拓展了repeat方法用于重复输出字符串
参数就是要重复的次数
返回值就是重复的结果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
// ES6中提供了一个repeat方法 用于实现重复字符串
console.log(str.repeat(4));
// 实现repeat方法
String.prototype.newRepeat = function(num) {
// 定义结果变量
let result = '';
// 遍历num次
for (let i = 0; i < num; i++) {
result += str;
}
// 返回结果
return result;
}
console.log(str.newRepeat(4));
</script>
</body>
</html>
2.4 判断字符串位置
startsWith(str, pos) 是否以参数字符串开头
截取后面的部分,并且包含截取位置字符
str 参数字符串(子字符串)
pos 字符串截取位置 pos本质使用slice实现的,slice传递一个参数,默认截取到结尾
返回值都是布尔值
endsWith(str, pos) 是否以参数字符串结尾
截取前面的部分,并且不包含截取位置字符
pos 本质是slice(0,pos),因此不包含pos
includes(str, pos) 是否包含参数字符串
截取后面的部分,并且包含截取位置字符
<!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>
<body>
<script>
// 定义字符串
// 0 1 2 3 4 5 6 78 910111213
let str = '这是一个很火的游戏-英雄联盟';
// startsWith(str, pos) 是否以参数字符串开头
// 截取后面的部分,并且包含截取位置字符
// 判断字符串是否在开头
// console.log(str.startsWith('这是'));
// console.log(str.startsWith('英雄', 10));
// 实现startsWith方法
// String.prototype.newStartWith = function(str, pos) {
// // 获取截取的字符串
// let subStr = this.slice(pos);
// console.log(subStr);
// // 定义正则表达式
// // let reg = new RegExp('^' + str);
// // console.log(reg);
// // 验证
// // return reg.test(subStr);
// // 通过字符串的某些方法验证
// return subStr.indexOf(str) === 0;
// }
// // 测试
// console.log(str.newStartWith('英雄', 11));
// endsWith(str, pos) 是否以参数字符串结尾
// 截取前面的部分,并且不包含截取位置字符
// console.log(str.endsWith('联盟'));
// console.log(str.endsWith('游戏', 9));
// // 实现endsWith方法
// String.prototype.newEndsWith = function(str, pos) {
// // 获取截取的字符串 就是在截取this
// let subStr = this.slice(0, pos);
// // 定义正则表达式
// // let reg = new RegExp(str + '$');
// // 验证
// // return reg.test(subStr);
// // 通过字符串的方式来验证
// // return subStr.slice(subStr.length - str.length) === str;
// return subStr.slice(-str.length) === str;
// }
// // 测试
// console.log(str.newEndsWith('联盟'));
// console.log(str.newEndsWith('游戏', 9));
// includes(str, pos) 是否包含参数字符串
// 截取后面的部分,并且包含截取位置字符
console.log(str.includes('游戏'));
console.log(str.includes('英雄', 10));
// 实现endsWith方法
String.prototype.newIncludes = function(str, pos) {
// 获取截取的字符
let subStr = this.slice(pos);
// 定义正则表达式
let reg = new RegExp(str);
// 验证
// return reg.test(subStr);
// 使用字符串的形式验证
return subStr.indexOf(str) >= 0;
}
// 测试
console.log(str.newIncludes('游戏'));
console.log(str.newIncludes('英雄', 10));
</script>
</body>
</html>
三、数字拓展
3.1 isNaN
ES6中为数字拓展了几个方法: isNaN、isFinite、 isInteger
全局中有一个isNaN方法,是用于判断是否是NaN(not a Number)
全局中isNaN在判断的时候,会进行类型转换
而Number拓展的isNaN,在判断的时候不会做类型转换
首先必须是数字
其次才去判断是否是NaN
如果是NaN,则返回true
如果不是NaN,则返回false
注意点::
// 这个false 是因为它是数字不属于NAN
console.log(window.isNaN('123'));// false
// 这个false 是因为它是字符串,不属于数字
console.log(Number.isNaN('123'));// false
3.2 isFinite
全局中有一个isFinite方法,是用于判断是否是有限的
全局中isFinite在判断的时候,会进行类型转换
而Number拓展的isFinite,在判断的时候不会做类型转换
首先必须是数字
其次才去判断是否是有限的
如果是有限的,则返回true
如果不是有限的,则返回false
注意::
// 计算机能够表示出 1 / 3的结果
console.log(window.isFinite(1 / 3));// true
3.3 isInteger
全局中没有该方法
Number拓展的isInteger方法,用于判断是否是整型的
在判断的过程中,会校验类型
首先必须是数字
其次才去判断是否是整型的
如果是整型,则返回true
如果不是整型,则返回false
注意::
// 因为计算机打印出来100.00的结果是100;
console.log(Number.isInteger(100.00));// true
// 数字拓展
// 通过0 1 得到 0 -0 Infinity -Infinity NaN
let num1 = 0 / 1;
let num2 = 0 / -1;
let num3 = 1 / 0;
let num4 = -1 / 0;
let num5 = 0 / 0;
// 全局中有一个isNaN方法,是用于判断是否是NaN(not a Number)
// 全局中isNaN在判断的时候,会进行类型转换
// console.log(window.isNaN(num4)); // false
// console.log(window.isNaN(num5)); // true
// console.log(window.isNaN('123')); // false
// console.log(window.isNaN(+'a123')); // true
// console.log(window.isNaN(parseInt('a123'))); // true
// // Math.floor不能处理下面的数字但是可以处理小数
// console.log(window.isNaN(Math.floor('1a23'))); // true
// console.log(window.isNaN(parseInt('12.3'))); // false
// 而Number拓展的isNaN,在判断的时候不会做类型转换
// console.log(Number.isNaN(num4)); // false
// console.log(Number.isNaN(num5)); // true
// console.log(Number.isNaN('123')); // false
// console.log(Number.isNaN(+'a123')); // true
// console.log(Number.isNaN(+'123')); // false
// console.log(Number.isNaN(parseInt('a123'))); // true
// // Math.floor不能处理下面的数字但是可以处理小数
// console.log(Number.isNaN(Math.floor('1a23'))); // true
// console.log(Number.isNaN(parseInt('12.3'))); // false
// 全局中有一个isFinite方法,是用于判断是否是有限的
// 全局中isFinite在判断的时候,会进行类型转换
// console.log(window.isFinite(num4)); // false
// console.log(window.isFinite(num5)); // false
// console.log(window.isFinite('123')); // true
// console.log(window.isFinite(+'a123')); // false
// console.log(window.isFinite(parseInt('a123'))); // false
// // Math.floor不能处理下面的数字但是可以处理小数
// console.log(window.isFinite(Math.floor('1a23'))); // false
// console.log(window.isFinite(parseInt('12.3'))); // true
// console.log(window.isFinite(100.00)); // true
// // 计算机能够表示出 1 / 3的结果
// console.log(window.isFinite(1 / 3)); // true
// 而Number拓展的isFinite,在判断的时候不会做类型转换
// console.log(Number.isFinite(num4)); // false
// console.log(Number.isFinite(num5)); // false
// console.log(Number.isFinite('123')); // false
// console.log(Number.isFinite(+'a123')); // false
// console.log(Number.isFinite(parseInt('a123'))); // false
// // Math.floor不能处理下面的数字但是可以处理小数
// console.log(Number.isFinite(Math.floor('1a23'))); // false
// console.log(Number.isFinite(parseInt('12.3'))); // true
// console.log(Number.isFinite(100.00)); // true
// // 计算机能够表示出 1 / 3的结果
// console.log(Number.isFinite(1 / 3)); // true
// Number拓展的isInteger方法,用于判断是否是整型的
console.log(Number.isInteger(num4)); // false
console.log(Number.isInteger(num5)); // false
console.log(Number.isInteger('123')); // false
console.log(Number.isInteger(+'a123')); // false
console.log(Number.isInteger(parseInt('a123'))); // false
// Math.floor不能处理下面的数字但是可以处理小数
console.log(Number.isInteger(Math.floor('1a23'))); // false
console.log(Number.isInteger(parseInt('12.3'))); // true
console.log(Number.isInteger(100.00)); // true
console.log(Number.isInteger(123.123)); // false
console.log(Number.isInteger(1 / 3)); // false
3.4 数学对象拓展
就是对Math对象的拓展,
ES6为了适应大型项目,解决自身运算的问题,拓展了大量的方法。
【关注下】Math.cbrt:计算一个数的立方根。
Math.fround:返回一个数的单精度浮点数形式。
Math.hypot:返回所有参数的平方和的平方根。
Math.expm1(x):返回ex - 1。
Math.log1p(x):返回1 + x的自然对数。如果x小于-1,返回NaN。
Math.log10(x):返回以10为底的x的对数。如果x小于0,则返回NaN。
Math.log2(x):返回以2为底的x的对数。如果x小于0,则返回NaN。
三角函数方法
Math.sinh(x) 返回x的双曲正弦(hyperbolic sine)
Math.cosh(x) 返回x的双曲余弦(hyperbolic cosine)
Math.tanh(x) 返回x的双曲正切(hyperbolic tangent)
Math.asinh(x) 返回x的反双曲正弦(inverse hyperbolic sine)
Math.acosh(x) 返回x的反双曲余弦(inverse hyperbolic cosine)
Math.atanh(x) 返回x的反双曲正切(inverse hyperbolic tangent)
【关注下】Math.sign 返回一个数字的标志,用来判断数字范围的
(0, Infinity] =>1,
[-Infinity, 0) => -1,
0 => 0, -
0 => -0,
NaN => NaN
四、对象拓展
4.1 对象字面量
对象字面量:let obj = {}
省略语法:
1 如果定义的属性名称与属性值变量同名,我们可以省略属性名称以及冒号
2 可以对属性名称书写表达式,通过[]来动态的设置属性名称
之前可以通过[]来获取属性,现在我们可以通过[]来设置属性
3 在对象中定义的方法可以省略冒号以及function关键字
<!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>
<body>
<script>
// 定义msg变量
let msg = 'hello';
// 定义对象
let obj = {
// msg: msg
// 1 如果定义的属性名称与属性值变量同名,我们可以省略属性名称以及冒号
msg,
// 2 可以对属性名称书写表达式,通过[]来动态的设置属性名称
// 之前可以通过[]来获取属性,现在我们可以通过[]来设置属性名
num: 500,
// []里面是一个js环境 可以书写表达式
[msg.toUpperCase()]: 'hello world',
// 之前定义方法
say: function() {
console.log('hello');
},
// 3 在对象中定义的方法可以省略冒号以及function关键字
sayHello() {
console.log('hello everyOne');
}
}
console.log(obj.say());
console.log(obj.sayHello());
</script>
</body>
</html>
4.2 is
is方法用于判断两个参数是否全等(===)
全等判断是有问题的,问题如下
1、0、- 0 之前在进行全等判断的时候,得到的是true
0 和- 0 之间是差了一个符号位,在二进制中,存储的数据是不同的
2、NaN 和 NaN 在进行全等判断的时候,得到的是false
所有NaN都表示“不是一个数字”,它们存储的地址是一样
对象拓展的is方法,就完善了上面的问题,方法如下
在判断0和-0的时候 得到的false
在判断NaN的时候 得到的就是true
<!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>
<body>
<script>
// 全等判断有几个问题:
// 1、0 - 0 之前在进行全等判断的时候,得到的是true
// 0 和- 0 之间是差了一个符号位,在二进制中,存储的数据是不同的
// console.log(0 / 1 === 0 / -1);
// 2、NaN 和 NaN 在进行全等判断的时候,得到的是false
// 所有NaN都表示“不是一个数字”,它们存储的地址是一样
// console.log(0 / 0 === +'a123');
// 对象拓展的is方法:
// 在判断0和-0的时候,得到的false
// 在判断NaN的时候,得到的就是true
// console.log(Object.is(0, -0));
// console.log(Object.is(0 / 0, +'a123'));
// 判断其它的都是一样的
console.log(Object.is(100, 100.00));
console.log(Object.is(123, +'123'));
console.log(123 === +'123');
</script>
</body>
</html>
4.3 assign
ES6拓展的assign是用于复制对象的,和jQuery、undescore中的extend方法类似
使用方式:Object.assign(obj, obj1, obj2)
obj: 被复制的目标对象 从第二个参数开始,都是复制的对象 返回值是目标对象obj
注意:后面对象中的同名属性会覆盖前面对象中的属性
assign方法实现的是一个浅复制
- 浅复制:值类型是直接复制,而引用类型是改变指向,(赋值赋的是地址)没有真正的复制
- 深复制:值类型是直接复制,引用类型也是直接复制,并不是改变指向(函数除外)
- =深复制就是一分为二,直接复制数值,谁也不影响谁
简单实现深复制: JSON.parse(JSON.stringify),但是转换json字符串的时候,会过滤掉函数
jQuery 中的 extend 方法第一个参数传递 true 的时候就是深复制
<!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>
<body>
<script src="./js/jquery.js"></script>
<script>
// 定义对象
let obj = {
name: 'xiaoming',
}
let obj1 = {
name: 'xiaohong',
age: 12
}
let obj2 = {
sex: '女',
colors: ['red', 'green', 'blue']
}
// 实现复制对象
// let result = Object.assign(obj, obj1, obj2);
// jQuery 中的 extend 方法第一个参数传递 true 的时候就是深复制
// let result = $.extend(true, obj, obj1, obj2)
// console.log(result);
// assign方法实现的是一个浅复制
// 对于值类型来说 是直接复制
// obj1.age = 13;
// 对于引用类型来说 是指针的改变
// obj2.colors.push('orange');
// console.log(result);
// 简单实现深复制: JSON.parse(JSON.stringify)
// let json = { a: 1, b: 2 };
// let newObj = JSON.parse(JSON.stringify(json));
// json.a = 100;
// console.log(newObj);
</script>
<script>
// 实现assign方法
// assign是Object的静态方法,因此是在Object中创建
Object.defineProperty(Object, 'assign2', {
// 配置值
value: function(target) {
// 开启严格模式
'use strict';
// 安全校验
if (target === null) {
throw new Error('不能传递null');
}
// 缓存对象
let to = Object(target);
// 遍历arguments
for (let i = 1; i < arguments.length; i++) {
// 定义对象
let obj = arguments[i];
// 遍历对象数据
for (let key in obj) {
// 判断数据是否存在对象自身
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// 复制
to[key] = obj[key];
}
}
}
// 返回结果
return to;
}
})
let result = Object.assign2(obj, obj1, obj2);
// 对于引用类型来说 是指针的改变
obj2.colors.push('orange');
console.log(result);
</script>
</body>
</html>
-
在上述中,在返回属性的时候用到了
Object.prototype.hasOwnProperty.call(obj, key)) -
为什么使用hasOwnProperty
- Object.keys() 方法会返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致(两者的主要区别是 for-in 还会遍历出一个对象从其原型链上继承到的可枚举属性)
-
为什么又要使用call
-
因为 javascript 没有将hasOwnProperty作为一个敏感词,所以我们很有可能将对象的一个属性命名为hasOwnProperty,这样一来就无法再使用对象原型的 hasOwnProperty 方法来判断属性是否是来自原型链。
-
var foo = { hasOwnProperty: function() { return false; }, bar: 'Hello' }; foo.hasOwnProperty('bar'); // 始终返回 false
-
五、数组拓展
5.1 from
用于遍历类数组对象,或将类数组对象转换成数组,是数组的静态方法。
类数组对象:
可以通过索引值获取属性值,
并且要具备length属性的一类对象
类数组对象不能使用数组的遍历器方法,ES6中拓展的from方法可以将类数组对象转为真正的数组,之后就可以使用数组的常用方法
使用方式: Array.from(arrLike, fn)
arrLike: 类数组对象
fn: 执行的函数,
有两个参数:成员值、索引值。
this默认指向window
如果传递的fn参数,此时,fn方法的返回值是from函数的执行结果
总结:from方法不仅可以将类数组转为数组,还可以遍历类数组对象
<!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>
<body>
<div>hello world</div>
<div>hello world</div>
<div>hello world</div>
<script>
// 获取类数组对象
let divs = document.getElementsByTagName('div');
// 类数组不能使用数组中的方法
// divs.forEach()
// 之前转为数组的方式: Array.prototype.slice.call(divs)
// let arr = Array.prototype.slice.call(divs);
// Es6中提供了from方法 将一个类数组转为数组
// let arr = Array.from(divs, function(item, index) {
// // console.log(this, arguments);
// item.style.color = 'red';
// // 返回结果
// return index + '-' + index;
// });
// console.log(arr);
// 实现from方法
Array.newFrom = function(arrlick, fn) {
// 定义空数组
let result = [];
// 遍历类数组对象
for (let i = 0; i < arrlick.length; i++) {
// 判断fn是否传递
if (fn) {
// 返回fn执行结果
result.push(fn(arrlick[i], i));
} else {
// 说明没有传递
result.push(arrlick[i]);
}
}
// 返回结果
return result;
}
// 测试:
let arr = Array.newFrom(divs, function(item, index) {
console.log(this, arguments);
item.style.color = 'red';
// 返回结果
return index + '-' + index;
});
console.log(arr);
</script>
</body>
</html>
浙公网安备 33010602011771号