1. let关键字
<body>
<button>测试1</button>
<br>
<button>测试2</button>
<br>
<button>测试3</button>
<br>
<!--
***let
1. 作用:
* 与var类似, 用于声明一个变量
2. 特点:
* 在块作用域内有效
* 不能重复声明
* 不会预处理, 不存在提升
3. 应用:
* 循环遍历加监听
* 使用let取代var是趋势
-->
<script type="text/javascript">
//console.log(age);// age is not defined
let age = 12;
//let age = 13;不能重复声明
console.log(age);
let btns = document.getElementsByTagName('button');
for(let i = 0;i<btns.length;i++){
btns[i].onclick = function () {
alert(i);
}
}
</script>
</body>
2. const关键字
<body>
<!--
1. 作用:
* 定义一个常量
2. 特点:
* 不能修改
* 其它特点同let
3. 应用:
* 保存不用改变的数据
-->
<script type="text/javascript">
// 以前定义常量的方式
var DATA_KEY = '12345678';
// 使用const定义常量
const PI = '3.141592654';
PI = '11';
console.log(PI); // Uncaught TypeError: Assignment to constant variable.
</script>
</body>
3. 变量的解构赋值
<body>
<!--
1. 理解:
* 从对象或数组中提取数据, 并赋值给变量(多个)
2. 对象的解构赋值
let {n, a} = {n:'tom', a:12}
3. 数组的解构赋值
let [a,b] = [1, 'atguigu'];
4. 用途
* 给多个形参赋值
-->
<script type="text/javascript">
let obj = { uname: 'zhangsan', age: 25 };
// let username = obj.uname;
// 使用ES6的解构赋值
let { username, age } = obj;
console.log(username, age); // undefined 25
// 等号的左边相当于是定义的全局变量,解构的名字必须是完全相同的(必须是对象本身已经存在的属性)
let { uname } = obj;
console.log(uname, age); // zhangsan 25
let arr = [1, 3, 5, 'aaa', true];
// 按照变量的顺序去取出数据
let [a, b, c, d, e, f] = arr;
console.log(a, b, c, d, e, f); // 1 3 5 "aaa" true undefined
// 使用,来占位置
let [, , g] = arr;
// 使用对象的解构赋值来获取参数信息
function foo({uname, age}){
console.log(uname, age); // zhangsan 25
}
foo(obj);
</script>
</body>
4. 模板字符串
<body>
<!--
1. 模板字符串 : 简化字符串的拼接
* 模板字符串必须用 `` 包含
* 变化的部分使用${xxx}定义
-->
<script type="text/javascript">
let obj = { username: 'zhangsan', age: 25 };
let str = `${obj.username}是一个傻瓜,年龄${obj.age}`;
console.log(str);
</script>
</body>
5. 简化的对象写法
<body>
<!--
简化的对象写法
* 省略同名的属性值
* 省略方法的function
* 例如:
let x = 1;
let y = 2;
let point = {
x,
y,
setX (x) {this.x = x}
};
-->
<script type="text/javascript">
let username = 'zhangsan';
let age = 18;
// 同名的属性可以省略不写
let obj = {
username,
age,
getName : function(){
return this.name;
},
// 可以省略函数的function
getAge(){
return this.age;
}
}
</script>
</body>
6. 深入理解箭头函数
<body>
<button id="btn1">测试箭头函数this_1</button>
<button id="btn2">测试箭头函数this_2</button>
<button id="btn3">测试箭头函数this_3</button>
<!--
* 作用: 定义匿名函数
* 基本语法:
* 没有参数: () => console.log('xxxx')
* 一个参数: i => i+2
* 大于一个参数: (i,j) => i+j
* 函数体不用大括号: 默认返回结果
* 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
* 使用场景: 多用来定义回调函数
* 箭头函数的特点:
1、简洁
2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
3、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。
4、箭头函数应用场景多用在回调函数中
-->
<script type="text/javascript">
// 1. 没有形参的时候
let fun = () => console.log('aaa');
// 2. 只有一个形参的时候, ()是可以省略的
let fun2 = a => console.log(a);
// 3. 两个及两个以上的形参的时候,()是不能省略的
let fun3 = (x, y) => console.log(x, y);
// 4. 函数体的情况
// 4.1 函数体只有一条语句或者是表达式的时候, {}可以省略,省略之后会自动返回语句执行的结果或者是表达式的结果
let func4 = (x, y) => x + y;
// 4.2 有{} 的情况,需要自动手动去返回
let fun5 = (x, y) => {
return x + y;
}
// 4.3 函数体有多条语句的时候, {}不可以省略
// 每一个函数都是有返回值的,如果没有手动指定返回值的话,就会默认返回undefined
let fun6 = (x, y) => {
console.log(x, y);
}
// 5. 箭头函数的this是他处于对象的内部
let btn1 = document.getElementById('btn1');
let btn2 = document.getElementById('btn2');
btn1.onclick = function () {
console.log(this);
};
// 6. 箭头函数this的深入理解.
let obj = {
name: '箭头函数',
// 普通函数的this指向是外部水调用的我,那么我的this指向的就是哪个对象
getName: function () {
// 终极理解方案:
// 1. 去看一下这个箭头函数的外部有没有函数了 如果这个函数的外部还是有函数的话,那么this的指向就是和外部的这个函数的环境是一致的,
// 否则this的指向就是指向的window对象
// 2. 发现外部有一个getName函数,这个函数是一个普通函数,那么这个函数的this就是和箭头函数的this一致的,我们发现这个函数放入this指向的是哪个调用者,
// 也就是只有obj对象调用我了之后,这个函数才可以向后执行,因此this就是执行obj对象,over。
btn2.onclick = () => {
console.log(this); // obj对象
}
}
}
obj.getName();
// 7. 这是一个按钮
let btn3 = document.getElementById('btn3');
let obj3 = {
name: '箭头函数',
getName: () => {
console.log('我是外部的this对象:', this); // window
btn3.onclick = () => {
// 分析思路:
// 1.先去看下这个函数是谁?这个函数是一个箭头函数,name去看下这个函数的外部有没有函数,如果有函数的话,那么里面的this就是和外边的this是一样的
// 如果外部没有函数的话,那么this就是window
// 2. 发现外部有一个函数getName,但是仔细一看这个函数竟然还是一个箭头函数,那么这个this的指向就是还是看他外部有没有函数,一看之后,外部没有函数了,那么这个
// 函数的内部的this指向就是widnow对象
console.log('我是最里面的this对象:', this); // this 是谁????, window对象
}
}
}
obj3.getName();
// 8. 继续剖析
let obj4 = {
name: '箭头函数',
getName: () => {
btn2.onmouseenter = () => {
console.log(this);
}
}
}
obj4.getName();
// 上面的代码等价于
obj4.getName = () => {
// ... window
}
</script>
</body>
7. 3点运算符
<body>
<!--
* 用途
1. rest(可变)参数
* 用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
function fun(...values) {
console.log(arguments);
arguments.forEach(function (item, index) {
console.log(item, index);
});
console.log(values);
values.forEach(function (item, index) {
console.log(item, index);
})
}
fun(1,2,3);
2. 扩展运算符
let arr1 = [1,3,5];
let arr2 = [2,...arr1,6];
arr2.push(...arr1);
-->
<script type="text/javascript">
// 使用arguments来获取参数信息
function foo() {
console.log(typeof arguments, arguments); // Arguments
// argument.callee() 本身也就是指向了函数本身, 应用于递归的场景,相当于是foo(), 再次调用了这个函数
// arguments.callee();
}
// 使用...作为参数, 如果前面有参数作为占位符的话,那么后面的部分就是我要使用的部分
// Rest parameter : 可变参数
function foo2(a, ...value) {
console.log(typeof value, value); // Array
// 由于此时value是一个数组类型,而不是一个伪数组,因此在这里就可以直接使用数组的方法了
value.forEach((item, index) => {
console.log(item, index);
});
}
foo();
foo2(10, 12);
// 应用场景,展开运算符
let arr = [1, 6];
let arr1 = [2, 3, 4, 5];
arr = [1, ...arr, 6]
console.log(arr);
// 数组元素的合并(灰常方便!!!)
let arr3 = [...arr, ...arr1];
console.log(arr3);
</script>
</body>
8. 形参默认值
<body>
<!--
* 形参的默认值----当不传入参数的时候默认使用形参里的默认值
function Point(x = 1,y = 2) {
this.x = x;
this.y = y;
}
-->
<script type="text/javascript">
// 可以使用形参的默认值,来实现构造函数参数的初始化操作
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
let point = new Point(23, 35);
console.log(point); // 23 35
let point1 = new Point();
console.log(point1); // undefined undefined
</script>
</body>
9. Promise对象
<body>
<!--
1. 理解:
* Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
* 有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
* ES6的Promise是一个构造函数, 用来生成promise实例
2. 使用promise基本步骤(2步):
* 创建promise对象
let promise = new Promise((resolve, reject) => {
//初始化promise状态为 pending
//执行异步操作
if(异步操作成功) {
resolve(value);//修改promise的状态为fullfilled
} else {
reject(errMsg);//修改promise的状态为rejected
}
})
* 调用promise的then()
promise.then(function(
result => console.log(result),
errorMsg => alert(errorMsg)
))
3. promise对象的3个状态
* pending: 初始化状态
* fullfilled: 成功状态
* rejected: 失败状态
4. 应用:
* 使用promise实现超时处理
* 使用promise封装处理ajax请求
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
}
request.responseType = 'json';
request.open("GET", url);
request.send();
-->
<script type="text/javascript">
// 创建一个promise对象
let promise = new Promise((resolve, reject) => {
// 初始化promise状态,pending: 初始化
// 这个函数是同步执行的, 因此函数的执行顺序是自上向下执行的
console.log(222);
// 异步操作,ajax请求
setTimeout(() => {
console.log(333);
// 然后根据异步任务返回的结果去修改promise的状态
// 异步任务执行成功
reject('404 error'); // 修改promise的状态为成功状态:fullfilled
resolve('success');
}, 2000)
});
console.log(1111);
promise.then((data) => {
console.log('执行成功!', data);
}, (error) => {
console.log('执行失败!', error);
});
// 获取数据信息
// Ajax 创建的三步骤:创建,open,send
function getNews(url) {
let promise = new Promise((resolve, reject) => {
// 1. 创建请求对象
let xmlHttp = new XMLHttpRequest();
console.log('init state: ', xmlHttp.readyState);
// 0 1 2 3 4 状态码,这个onreadystateChange这个函数会调用4次
xmlHttp.onreadystatechange = () => {
// 4 表示的是数据成功了, 200 表示成功或者失败的状态码
if (xmlHttp.readyState === 4) {
// 请求数据完成的时候
if (xmlHttp.status === 200) {
// success
console.log(xmlHttp.responseText);
resolve(xmlHttp.responseText); // 数据获取成功, 返回获取从成功的数据信息
} else {
// error
console.log(xmlHttp.responseText);
resolve(xmlHttp.responseText); // 数据获取成功, 返回获取从成功的数据信息
}
}
}
// 2. 创建open, 指定请求的方式为true或者false
xmlHttp.open('GET', url);
// 3.发送
xmlHttp.send();
});
return promise;
}
// 获取新闻的内容信息, 只要前面的数据信息获取失败了的话,那么后面的函数就是不会执行的
getNews('http://localhost:3000/news?id=2').then((data) => {
console.log('get data success!', data);
// 开始获取数据信息,发送第二次请求
let url = 'http://localhost:3000' + JSON.parse(data).commentsUrl;
// 为了实现代码的解耦,可以把当前的promise返回出去
return getNews(url);
console.log(url);
}, (error) => {
console.log(error);
}).then((data) => {
console.log('comments data load finished: ', data);
}, (error) => {
console.log('error', error);
})
</script>
</body>
10. Symbol的使用
<body>
<!--
前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境
Symbol:
概念:ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题
2、Symbol值不能与其他数据进行计算,包括同字符串拼串
3、for in, for of遍历时不会遍历symbol属性。
使用:
1、调用Symbol函数得到symbol值
let symbol = Symbol();
let obj = {};
obj[symbol] = 'hello';
2、传参标识
let symbol = Symbol('one');
let symbol2 = Symbol('two');
console.log(symbol);// Symbol('one')
console.log(symbol2);// Symbol('two')
3、内置Symbol值
* 除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.iterator
* 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(后边讲)
-->
<script type="text/javascript">
// 创建一个symbol
let symbol = Symbol();
console.log(typeof symbol);
let obj = {name: 'zhangsan', age: 30};
// 给对象添加属性
obj[symbol] = 'hahaha';
console.log(obj);
// symbol 的比较
let symbol01 = Symbol('one');
let symbol02 = Symbol('two');
console.log(symbol01 === symbol02, symbol01, symbol02);
// symbol定义常量
const PERSON_KEY = Symbol('person_key');
console.log(PERSON_KEY);
// for in 遍历对象的属性,for of 都是无法遍历出来symbol的属性的
for (let item in obj) {
console.log(item, obj[item]);
}
let arr = [11, 2 , 3,3, 4, 4]
// for of 可以用来遍历数据每一项
for (let item of arr) {
console.log(item);
}
</script>
</body>
11. Iterator遍历器
<body>
<!--
概念: iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
作用:
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列
3、ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
工作原理:
- 创建一个指针对象(遍历器对象),指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
* value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
* 当遍历结束的时候返回的value值是undefined,done值为false
原生具备iterator接口的数据(可用for of遍历)
1、Array
2、arguments
3、set容器
4、map容器
5、String
。。。
-->
<script type="text/javascript">
// 创建一个遍历器对象
function myIterator(arr) { // iterator 接口
let nextIndex = 0; // 记录当前指针的位置
return {
// 遍历器对象
next() {
// 1. 函数嵌套 2. 内部函数引用外部函数的变量 3. 内部方法外部调用
return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true}
}
}
}
// 准备数据
let arr = [1, 20, 3, 4];
let iteratorObj = myIterator(arr);
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
console.log(iteratorObj.next());
// 如果没有值的话,默认返回的是undefined
console.log(iteratorObj.next());
console.log('======================================================================');
// 将iterator接口部署到指定的数据类型上,就可以直接使用for of去循环遍历
// 可以遍历的对象: 数组,字符串,arguments,Set容器,map容器
for (let item of arr) {
console.log(item);
}
console.log('======================================================================');
let str = 'aadsfsdfdfgfdg';
for(let item of str) {
console.log(item);
}
console.log('======================================================================');
function func() {
for (let item of arguments) {
console.log(item);
}
}
func(1, 2, 3, 'aa')
// 普通对象是不能进行迭代遍历的,也就是不能使用 for of去遍历的
console.log('======================================================================');
let obj = {
name : 'zhansdan',
age : 18
}
for (let item of obj) {
console.log(item); // Uncaught TypeError: obj is not iterable
}
console.log('symbol======================================================================');
// 在指定的数据结构上面部署了一个iterator接口,当使用for of去遍历某一个数据结构的时候,首先去找Symbol.iterator
// 如果找到了的话,就去遍历;没有找到的话就是不可以遍历的
let targetData = {
// Symbol 的迭代器变量
[Symbol.iterator] : function(){
let nextIndex = 0;
return {
next(){
// 遍历当前的对象
return nextIndex < this.length ? {value : this[nextIndex++], done : false} : {value : undefined, done : true}
}
}
}
}
// 使用三点运算符,解构赋值,默认去调用iterator接口
let arr = [1,2];
let arr2 = [2, 4];
console.log(...arr, ...arr2);
</script>
</body>
12. Generator函数
<body>
<!--
Generator函数
概念:
1、ES6提供的解决异步编程的方案之一
2、Generator函数是一个状态机,内部封装了不同状态的数据,
3、用来生成遍历器对象
4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
特点:
1、function 与函数名之间有一个星号
2、内部用yield表达式来定义不同的状态
例如:
function* generatorExample(){
let result = yield 'hello'; // 状态值为hello
yield 'generator'; // 状态值为generator
}
3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑
4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
5、再次调用next方法会从上一次停止时的yield处开始,直到最后
6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
-->
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
// 创建一个generator函数
// Symbol(Symbol.toStringTag) : "Generator 这个实现内部原理实际上是这个对象的原型上面的一个标识,使用了Generator来进行标识这个对象的
// 由于加上yield可以暂停,因此就是一个惰性求值函数;next函数可以用来再次执行函数
function* myGenerator() {
console.log('start');
// 遇见了yield先暂停,然后返回出去一个对象{hello, false}
let result = yield 'hello';
console.log('=======================result now is : ', result); // undefined, 传递过来的参数信息可以作为传递过来的那个参数的返回值
// 没有返回值的调用函数aaa {undefined, false}
yield console.log('aaa')
console.log('pause and start');
// {value: "generator", done: false}
yield 'generator';
console.log('finished');
// return 的默认值就是undefined
return '返回值'
};
// 直接调用是不能调用的,没有效果的
let MG = myGenerator(); // 这是一个指针对象
console.log(MG);
console.log(MG.next('aaaaaaaa-----bbbbbbbb')); // {value: "hello", done: false}
console.log(MG.next('bbbbbbbbbb')); // {value: "hello", done: false}
console.log(MG.next()); // {value: "hello", done: false}
console.log(MG.next()); // {value: undefined, done: true}
console.log(MG.next()); // {value: undefined, done: true}
// 对象的symbol.iterator属性 指向遍历器对象
let obj = {uasename: 'zhangsan', age: 39};
// 使用iterator中的迭代器对象,人为部署一个generator接口
obj[Symbol.iterator] = function* myGeneratorFunc() {
yield 1
yield 2
yield 3
}
/*for (let item of obj) {
console.log(item);
}*/
let myFunc = obj[Symbol.iterator]();
console.log(myFunc.next().value);
console.log(myFunc.next().value);
console.log(myFunc.next().value);
console.log(myFunc.next().value);
// 综上:通过给一个对象手动指定一个Symbol.iterator,可以实现for of对一个普通对象的遍历操作
// 1. 内部的实现原理实际上是使用了generator函数的next方法完成了对象的遍历操作
// 2. 遍历方式也就是obj[Symbol.iterator]的获取的是一个generator函数的引用,然后通过obj[Symbol.iterator]() 来调用函数
// 3. 接着通过obj[Symbol.iterator]().next() 方式来实现数据的获取,得到的起始是yield位置执行完毕只有的返回值{value : aaa, done : false}的格式
// 4. 最后通过obj[Symbol.iterator]().next().value 就可以实现普通对象的数据,通过iterator方式获取了
// 案例练习
/*
* 需求:
* 1、发送ajax请求获取新闻内容
* 2、新闻内容获取成功后再次发送请求,获取对应的新闻评论内容
* 3、新闻内容获取失败则不需要再次发送请求。
*
* */
// 使用Generator函数去异步获取数据
function getNews(url) {
$.get(url, function (data) {
console.log(data);
let url = 'http://localhost:3000' + data.commentsUrl;
console.log(url);
// 1. 在这里去向下移动指针 这里数据获取成功之后开始进行下一次数据的获取,这里把URl作为参数传递给下一个genrator函数
// 2. 那么这里的参数url就是上一次执行完毕的一个返回值
// 3. 如果之前的URL出错了,那么程序就不会向下执行了,回调函数就不会向下继续执行了
generator.next(url);
})
}
// getNews('http://localhost:3000/news?id=1')
// 使用generator函数实现数据的获取
function* sendReq() {
// next()中传递的参数,可以当做下一次的启动的返回值的
let url = yield getNews('http://localhost:3000/news?id=1');
console.log('-------------current request url is ', url); // http://localhost:3000/comments?newsId=1
yield getNews(url);
}
// 获取遍历器对象
let generator = sendReq();
console.log('-------------------', generator.next());
</script>
</body>
13. async函数
<body>
<!--
async函数(源自ES2017)
概念: 真正意义上去解决异步回调的问题,同步流程表达异步操作
本质: Generator的语法糖
语法:
async function foo(){
await 异步操作;
await 异步操作;
}
特点:
1、不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
2、返回的总是Promise对象,可以用then方法进行下一步操作
3、async取代Generator函数的星号*,await取代Generator的yield
4、语意上更为明确,使用简单,经临床验证,暂时没有任何副作用
-->
<script type="text/javascript" src="./js/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
// async =======================================================================
async function foo() {
return new Promise(resolve => {
// 普通写法
/*setTimeout(()=>{
resolve();
}, 2000)*/
// 上面的代码优化之后
setTimeout(resolve, 2000);
});
}
/**
* 实现异步的执行
* @return {Promise<void>}
*/
async function test() {
console.log('开始执行', new Date().toTimeString());
await foo();
console.log('开始执行', new Date().toTimeString());
}
// test();
// 普通的函数 ==============================================================
function test02() {
return 'return value : ' + new Date().toTimeString();
}
async function asyncPrint() {
let result = await test02();
// 传递的参数就是我的返回值
let result02 = await Promise.resolve('promise');
// 失败的状态信息
let result03 = await Promise.reject('失败的状态信息');
console.log('result now is : ' + result, 'result02:', result02, 'result03:', result03);
}
// asyncPrint();
// 获取新闻内容===============================================================
async function getNews(url) {
return new Promise((resolve, reject) => {
$.ajax({
method: 'GET',
url,
success: data => resolve(data), // 调用resolve的时候,把参数传递进去即可,resolve中传递的参数就是await的返回值
// error: error => reject(error) // 【注意点】:这里也是可以通过直接返回resolve的方式继续向下解析内容信息
error: error => resolve(false)
})
})
}
async function sendMessage() {
// 异步自动向下执行
let result = await getNews('http://localhost:3000/news1?id=2');
if (!result) {
alert('数据拉取失败,……………………');
return;
}
console.log('get news finished now : ', result); // result 中获取的实际上就是await的返回值
result = await getNews('http://localhost:3000' + result.commentsUrl);
console.log('get comments finished now : ', result); // result 中获取的实际上就是await的返回值
}
let res = sendMessage();
console.log(res);
</script>
</body>
14. class类的基本使用
<body>
</body>
<!--
1. 通过class定义类/实现类的继承
2. 在类中通过constructor定义构造方法
3. 通过new来创建类的实例
4. 通过extends来实现类的继承
5. 通过super调用父类的构造方法
6. 重写从父类中继承的一般方法
-->
<script type="text/javascript">
/**
* Person class
* @param name
* @param age
* @constructor
*/
function Person(name, age) {
this.name = name;
this.age = age;
}
console.log(new Person('zhangsan', 12));
// 类的所有方法都会放在它的原型对象上面(实例对象上面的方法都是给实例本身使用, 所有的实例对象都是可以放在原型对象里面)
class Animal {
// 这是类的构造方法
constructor(name, age) {
this.name = name;
this.age = age;
}
// 这个方法是放在Animal这个类的原型上面
showName() {
return this.name;
}
}
class Dog extends Animal {
constructor(name, age, sex) {
// 1. super 的目的是去调用父类的构造方法
// 2. 关键点:为了去调用父类的构造方法,这里需要手动在子类中把参数传递给父类去使用, 否则子类的就是一个{name : undefined, age : undefined, sex : 'params'}
super(name, age); // 这句话调用的目的就是相当于:this.name = name;this.age = age;
this.sex = sex; // 当依赖不上父类的时候,就需要自己去实现一个手动的属性
}
// 子类的方法重写父类的方法
showName() {
return this.name + ' ' + this.age + ' ' + this.sex;
}
}
let dog = new Dog('redDog', 25, 'Male'); // {name: "redDog", age: 25, sex: "Male"}
console.log(dog, dog.showName());
</script>
15 . 字符串/数值/数组/对象的方法扩展
15.1 字符串扩展
<body>
<!--
1. includes(str) : 判断是否包含指定的字符串
2. startsWith(str) : 判断是否以指定字符串开头
3. endsWith(str) : 判断是否以指定字符串结尾
4. repeat(count) : 重复指定次数
-->
<script type="text/javascript">
let str = 'afsfsfsdsdgsdggdwerewrsf';
console.log(str.includes('t'));
console.log(str.includes('a'));
console.log(str.startsWith('a'))
console.log(str.endsWith('b'))
// str.repeat 用于将当前的字符串重复5次输出
str = '123';
console.log(str.repeat(5))
</script>
</body>
15.2 数值扩展
<body>
<!--
1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
-->
<script type="text/javascript">
console.log(0b11); // 二进制输出11
console.log(0o56); // 八进制输出56, 5*8^1 + 6 *8^0 = 46.
let aaa = Infinity;
console.log(Number.isFinite(1/0), aaa); // true
console.log(Number.isNaN('ss')); // false
console.log(Number.isInteger(10.0)); // true
console.log(Number.isInteger(10.1)); // false
// 【parseInt的几个注意点:】
console.log(Number.parseInt('1212abc12')); // 1212 (只要一个字符串的前面部分有数字的部分,就会直接把这个字符串转换为一个数字)
console.log(Number.parseInt('abc12abc')); // NAN 开头的部分如果不是数字的话,就会直接把这个转换为一个NAN
console.log(Math.trunc(123.123)); // 123 (去除了小数点)
</script>
</body>
15.3 数组扩展
<body>
<button>测试1</button>
<br>
<button>测试2</button>
<br>
<button>测试3</button>
<br>
<!--
1. Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
2. Array.of(v1, v2, v3) : 将一系列值转换成数组
3. find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
4. findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
-->
<script type="text/javascript">
// 获取到的是一个伪数组对象
let btns = document.getElementsByTagName('button');
// 使用原始的方法
let arr = Array.prototype.slice.call(btns);
console.log(arr, btns);
console.log(arr.forEach((item) => item));
// 使用ES6的方法
Array.from(btns).forEach((item, index) => {
console.log(item, index);
})
// 转换伪数组
let arr01 = Array.of(1, 4, 'abc', 'def');
console.log(arr01);
// 查找数组中的元素, find 查找之后返回的是一个新的元素,而不是数组
let arr02 = [1, 2, 2, 3, 3, 4, 4]
let result = arr02.find((item, index) => {
console.log(item, index);
return item > 3;
});
console.log(result);
// 找出第一个满足条件的数组下标
console.log('---------------------------------------------------------')
let index = arr02.findIndex((item, index) => {
return item > 3;
});
console.log(index); // 5
</script>
</body>
15.4 对象扩展
<body>
<!--
1. Object.is(v1, v2)
* 判断2个数据是否完全相等
2. Object.assign(target, source1, source2..)
* 将源对象的属性复制到目标对象上
3. 直接操作 __proto__ 属性
let obj2 = {};
obj2.__proto__ = obj1;
-->
<script type="text/javascript">
// 对象测试
console.log(0 == -0); // true
console.log(NaN == NaN); // false, 注意NaN每次输出都是不同的值,因此不相同
console.log(NaN); // NaN
// 对象输出测试,Object.is实际上是以【字符串】的方式来进行判断的
console.log(Object.is(0, -0)); // false
console.log(Object.is(NaN, NaN)); // true
// 将源对象的属性复制到目标对象上面
let obj = {};
let obj1 = {username: 'zhangsan', age: 48};
Object.assign(obj, obj1);
console.log(obj); // {username: "zhangsan", age: 48}
let obj2 = {sex: 'MALE'};
Object.assign(obj, obj2);
console.log(obj); // {username: "zhangsan", age: 48, sex: "MALE"}
// 3. 直接操作__proto__, prototype 指向的是一个原型对象
let obj3 = {};
let obj4 = {money: 1500000};
obj3.__proto__ = obj4; // obj4 就是obj3的父类, 直接使用__proto__来实现
console.log(obj3, obj3.money)
</script>
</body>
16. 深拷贝和浅拷贝的对比
<body>
<!--
1、数据类型:
* 数据分为基本的数据类型(String, Number, boolean, Null, Undefined)和对象数据类型
- 基本数据类型:
特点: 存储的是该对象的实际数据
- 对象数据类型:
特点: 存储的是该对象在栈中引用,真实的数据存放在堆内存里
2、复制数据
- 基本数据类型存放的就是实际的数据,可直接复制
let number2 = 2;
let number1 = number2;
- 克隆数据:对象/数组
1、区别: 浅拷贝/深度拷贝
判断: 拷贝是否产生了新的数据还是拷贝的是数据的引用
知识点:对象数据存放的是对象在栈内存的引用,直接复制的是对象的引用
let obj = {username: 'kobe'}
let obj1 = obj; // obj1 复制了obj在栈内存的引用
2、常用的拷贝技术
1). arr.concat(): 数组浅拷贝
2). arr.slice(): 数组浅拷贝
3). JSON.parse(JSON.stringify(arr/obj)): 数组或对象深拷贝, 但不能处理函数数据
4). 浅拷贝包含函数数据的对象/数组
5). 深拷贝包含函数数据的对象/数组
-->
<script type="text/javascript">
// 修改str2的数据不会影响到原始数据
let str = 'aaa';
let str2 = str;
console.log(str2); // aaa
str2 = '';
console.log(str); // aaa
let bool1 = true;
let bool2 = bool1;
bool2 = false;
console.log(bool1); // true
// 使用引用
let obj = {username: 'zhangsan', age: 35};
let obj1 = obj;
console.log(obj1);
obj1.username = 'lisi';
console.log(obj);
// 数组传值
let arr = [1, 4, {username: 'zhaoliu', age: 18}];
let arr2 = arr;
arr2[0] = 'abcdefg'; // 会把原来的数据也修改了
console.log(arr, arr2);
// 总结:
/*
* 基本数据类型:
* 1. 拷贝之后会生成一份新的数据,修改拷贝之后的数据不会修改到原始数据
* 2. 对象/数组拷贝之后不会生成新的数据,而会是一个对象的引用,修改拷贝之后的数据也会影响到原始的数据信息
* 拷贝数据的基本方法:
* 1. 直接复制给一个变量 (浅拷贝)
* 2. Object.assign() (浅拷贝)
* 3. Array.prootype.comcat() (浅拷贝)
* 4. Array.prototype.slice() (浅拷贝)
* 5. JSON.parse(JSON.stringfy()) (深拷贝:重要点,是不能处理函数的)
*
* 浅拷贝和深拷贝(只针对对象和数组):
* 浅拷贝:拷贝的是对象的引用,修改拷贝之后的数据会影响到原始的数据
* 深拷贝(深度克隆):拷贝的是数据,拷贝的时候会生成一份新的数据,修改拷贝之后的数据不会影响到原始的数据
* */
console.log('深拷贝和浅拷贝 =============================================================');
// Object.assign实现拷贝数据
let obj001 = {username: 'liuyu', age: 18};
let obj002 = Object.assign(obj001); // 浅拷贝
console.log(obj002);
obj002.username = 'zhangsanlisiwangwu';
console.log(obj002);
// 实现数组的拷贝
console.log('数组的拷贝-------------------------------------------------');
let arr001 = [1, 1, 2, 2, 323, 40, {
username: 'zhaoliu',
getName() {
console.log("I am a function!");
// ... 对象里面的一个函数, 在进行JSON.parse的时候是不能把这个函数也来拷贝一份的!!!
}
}, function () {
console.log('function here!')
}];
let arr002 = arr001.concat();
console.log(arr002);
arr002[1] = 'hahaha';
arr002[6].username = 'Object was updated!';
console.log(arr001);
// slice
// let arr003 = arr001.slice(startIndex, endIndex)
let arr003 = arr001.slice(); // 不传递参数的时候,实际上截取的是一个数组本身原封不动地复制了一份。
console.log(arr003);
arr003[6].username = 'I am a slice func!';
console.log(arr001);
// 深度拷贝
console.log('数组的深度拷贝-------------------------------------------------');
// 实现原理:实际上是中间的部分把一个对象转换为了一个字符串,然后相当于是数据类型
let arr004 = JSON.stringify(arr001);
console.log(arr004);
// 开始实现深度拷贝一个对象
// 【关键点:】JSON.stringfy()只可以把一个JS对象转换为JSON字符串,也就是只可以转换{}和Array([])这两种数据类型
let parse = JSON.parse(JSON.stringify(arr001));
parse[6].username = 'I am deep clone!'
// 拷贝之后,发现原来arr001对象里面函数function不见了,去哪了???
console.log(arr001, parse); // 深度拷贝的实现原理??
// 深度拷贝高级知识【高级】======================================================================================================================
console.log('深度拷贝=================================================================================================')
/*
* 如何实现深度拷贝:
* 1. 拷贝的数据里面不能有对象/数组,即使有的话,也可以继续去遍历对象,或者数组,获取到对象的每一个值,然后再去复制,就是深度拷贝!
* 2. 知识点储备:
* 2.1 typeof返回是数据类型:String, Number, Boolean, Undefined, Object, Function【重点】
* 2.2 Object.prototype.toString.call(obj)
* */
let result = 'abcd';
console.log(Object.prototype.toString.call(result)); // [object String]
result = null;
console.log(Object.prototype.toString.call(result)); // [object Null]
result = [];
console.log(Object.prototype.toString.call(Array)); // [object Null]
console.log(typeof Object.prototype.toString.call(Array)); // string
let type = Object.prototype.toString.call(result);
// slice(startIndex, endIndex), 注意: 这里是不包含endIndex 的位置的,因此可以直接从后往前数-1的位置就行
console.log(type.slice(8, -1));
let arr0001 = [12, 3, 34, 4, 4]
for (let item in arr0001) {
console.log(item); // 数组遍历的时候,遍历的是数组的下标index; 枚举对象的时候枚举的是对象的key
}
// 检测数据类型的功能函数
/**
* 数据类型通用检测
* @param target
* @return {string}
*/
function checkType(target) {
// 返回的就是一个类型字符串, 末尾的位置
return Object.prototype.toString.call(target).slice(8, -1);
}
console.log(checkType([]));
/**
* 实现一个深度克隆函数
* @param target
*/
function clone(target) {
// ... 只针对对象和数组这两种数据类型
// result 是一个最终克隆出来的数据
let result, targetType = checkType(target);
if (targetType === 'Object') {
result = {};
} else if (targetType === 'Array') {
result = [];
} else {
// 如果是字符串或者其他的普通基本数据类型的话
return target;
}
// 遍历目标数据
for (let item in target) {
// 获取target中的每一项数据(不管你是对象或者数组都是可以的)
// 对象的时候item是一个key;数组的时候是一个index数组下标
let value = target[item];
if (checkType(value) === 'Object' || checkType(value) === 'Array') {
// ... 继续递归遍历获取到的value值
result[item] = clone(value);
} else {
// 如果获取的到的数据是一个基本数据类型的话
result[item] = value;
}
}
return result;
}
// arr
let arr00001 = [1, 2, 3, 4, {username: 'deep clone'}];
let data00001 = clone(arr00001);
console.log(data00001);
data00001[4].username = 'hahahaha';
console.log(arr00001, data00001);
// obj
let obj000001 = {username: 'liudehua', age: 28};
console.log(obj000001);
let data0002 = clone(obj000001);
data0002.username = '11212';
console.log(data0002);
</script>
</body>
17. Set和Map数据结构
<body>
<!--
1. Set容器 : 无序不可重复的多个value的集合体
* Set()
* Set(array)
* add(value)
* delete(value)
* has(value)
* clear()
* size
2. Map容器 : 无序的 key不重复的多个key-value的集合体(数组集合)
* Map()
* Map(array)
* set(key, value)//添加
* get(key)
* delete(key)
* has(key)
* clear()
* size
-->
<script type="text/javascript">
// set 中存放的是不可重复的数据
let set = new Set([1, 2, 3, 3, 4, 34, 234, 23]);
console.log(set);
set.add(10);
console.log(set.size, set);
set.delete(0);
console.log(set.has(1));
set.clear();
// map 的使用(存放的是一个二维数组)
let map = new Map([['name' , 'zhangsan'], ['age', '18']]);
console.log(map);
map.set('sex' , 'MALE'); // 添加元素
console.log(map);
map.delete('sex');
console.log(map);
</script>
</body>
18. for_of循环
<body>
<!--
for(let value of target){}循环遍历
1. 遍历数组
2. 遍历Set
3. 遍历Map
4. 遍历字符串
5. 遍历伪数组
-->
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script type="text/javascript">
let set = new Set([13, 34324, 2342345, 235, 234543, 534, 5, 5]);
for (let item of set) {
console.log(item);
}
// set 实现一个数组去重
let arr = [1, 3, 4545, 2, 3, 4];
let arr1 = arr;
arr = [];
let set01 = new Set(arr1);
// 开始去重
for (let item of set01) {
arr.push(item);
}
console.log(arr);
</script>
</body>