高级js
全为重要内容
对象是什么? 对象是属性和方法的集合体!
prototype!!!
- 是什么:属性名;构造函数的属性名;内置的,不是我们写的;
- 有什么特别之处:
- 属性值是个对象,这个对象上可以添加很多方法(公共的方法);
- 每个实例化对象都可以访问上面的方法,解决上面内存浪费的问题;
- 官方名:构造函数的原型;
- 规则:
-
JavaScript 规定,每一个构造函数都有一个prototype 属性名,对应的值保存地址,指向一个对象。
-
我们可以把那些公共的方法,直接定义在 prototype 对象上,这样所有对象实例就可以共享这些方法。
function Star(uname, age) {
this.uname = uname;
this.age = age;
}// 记住:属性名prototype 属性值对象{} ----> Star.prototype = {} 不需要我们赋值;
// 语法:定义
Star.prototype.sing = function() {
console.log(‘我会唱歌’);
}var ldh = new Star(‘刘德华’, 18);
var zxy = new Star(‘张学友’, 19);// 记住:虽然方法都是定义在 prototype属性值 对象上;但是实例化可以直接使用;
// 这个方法都是去原型对象上找方法;使用是同一个方法;
ldh.sing();//我会唱歌
zxy.sing();//我会唱歌// 验证:
console.log(ldh.sing === zxy.sing); // true
-
this指向!!!
- 位置:函数内部;
- this到底指向谁?规则:谁调用,this就是指谁!!
- 场景1:事件绑定
自己理解 函数的this如果不new一个实例 那么this 指向是BOM 此时this指向 window
如果new了一个实例化对象 此时this指向的就是实例化对象
第二天
constructor 面试可能会问
constructor:
位置名:来自原型对象上默认位置名值:构造函数本身
意义:有个实例化对象,实例化.<<底杠>>proto<<底杠>>.constructor 找到这个实例化是由哪个构造函数!
代码:覆盖与否对代码开发没有任何影响;
原型链
查找机制:
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
- 如果没有就查找它的原型对象(也就是 proto指向的 prototype 原型对象)。
- 如果还没有就查找原型对象的原型(Object的原型对象)。
- 依此类推一直找到 Object 为止(null)。
- 如果还没有,就报错这个对象没有这个方法;Uncaught TypeError: XXX.fn is not a function
特点:
// 每一个实例化对象 都会有proto 非标准属性,指向其Star构造函数的.prototype
// 原型对象也是个对象;也是某个构造函数的一个实例化,也有相应的proto,指向其 构造函数的.proto
继承!!!
- 为什么学习继承?
- 有的构造函数上原型对象上已经实现一部分方法,
- 我们需要这些方法,就需要把这个部分功能继承过来(内存地址上指向是同一个地址),而不是重新再写一次;
- 大白话:想办法把别人原型对象 串 我们自己原型链;
继承-01-构造函数内部属性 call
新的语法:一次性把对方身上所有属性全部拿过!
call:打电话 呼叫;基础语法
function Other(house, money, car) {
this.house = house;
this.money = money;
this.car = car;
}
var obj = {
info: "我就是个单独对象",
};
Other.call(obj, 10, 20, 10);
// call语法规则:【需要重点记忆】
// 1.Other函数肯定是要执行!
// 2.参数:第一个参数 Other执行,Other内部this上的所有的属性名 即将要去到 这个参数上;
// 剩余参数,必须逗号分隔;就是OTher执行时,需要传入的实参;
console.log(obj);
----------------------------------------------------------------------------------------
// 构造函数的内部数据的继承:
function Other(house, money, car) {
this.house = house;
this.money = money;
this.car = car;
}
function My2(a, b, c) {
// 这个My2构造函数内部的实例化对象
Other.call(this, a, b, c);
// 相当于:
// this.house = a;
// this.money = b;
// this.car = c;
}
var m1 = new My2(10, 50, 60);
console.log(m1);
var m2 = new My2(20, 20, 20);
console.log(m2);
继承-02-构造函数的原型对象
// 别人写好构造函数+原型对象
function Other(house) {
this.house = house;
}
Other.prototype.show = function() {
console.log("你看老子多有钱,我有" + this.house + "套房子");
};
Other.prototype.sing = function() {
console.log("你看老子多有钱,我会唱歌");
};
// 自己:
function My(house) {
Other.call(this, house);
}
// --------------------------------------------------------------------方式3:
//
My.prototype = new Other();
// My.prototype 本质是 {} __proto__ : Object的原型对象;自带了constructor:My;
// 想被替换为 {} __proto__本来就是指向Other原型对象;
// o = new Other(); 就完成代替任务;{} __proto__本来就是指向Other原型对象;
My.prototype.constructor = My;
// 对上面的分解
// var o = new Other(50); // o {house:50 ,__proto__:Other.prototype}
// My.prototype = o;
My.prototype.dance = function() {
console.log("我是My原型对象上一个方法");
}
// 测试My
var m = new My(10);
// m.dance();
// m.show();
// m.sing();
console.log(m.house);
// 测试Other:
// var o = new Other(60);
// o.show();
// o.sing();
// o.dance();
// console.log(o);
ES6 类 基本语法
// ES6:类型 构造函数相关:
// 语法:class关键字 My类的名称(构造函数的名称)
class My {
constructor(name, age) {
//特点1:构造器constructor函数,不需要写function,函数末尾没有逗号
this.name = name;
this.age = age;
}
show() {
//特点2:下面直接写原型对象上方法就OK,prototype不出现
console.log("我是个炫富的方法");
console.log(this.name);
}
}
// 使用:和过去是一样;实例化
var m = new My("zs", 18);
console.log(m);
ES6-类-继承-语法
extends 拓展;一个单词全部 属性和方法继承过来
// ES6 类的语法
class Other {
constructor(house, car) {
this.house = house;
this.car = car;
}
show() {
console.log(`你看,老子很有钱的!我有${this.house}套房子,有${this.car}辆车`);
}
fn() {
console.log("我是other新的方法");
}
}
class My extends Other {
// My天地:
}
var m = new My(10, 10);
console.log(m);
ES6-类-继承-super
// Other
class Other {
constructor(car, house) {
// this.car = car;
// this.house = house;
}
show() {
console.log("我有很多钱,有" + this.car + "辆车," + this.house + "套房子");
}
}
// 自己类:
class My extends Other {
// 除了继承过来的属性名 car house; 想添加一些自己的属性;
constructor(a, b, money) {
// 记住:
// 虽然extends可以继承Other属性,但是如果 My要自己定义一些属性的话,需要关键字 super;把继承过来 car house属性 设置一个对应的值;至于来自变量哪个位置,无所谓!
// super调用 必须在 this 之前!不然就报错!
// 如果被继承Other没有属性,必须安排 super()!
super();
this.money = money;
}
}
// 测试
var m1 = new My(55, 66, 1);
console.log(m1);
class Other {
constructor(house, car) { // es5构造函数
this.house = house; //未来某一次实例化对象
this.car = car;
}
show() {
console.log(`你看:老子有 ${this.house} 套房子;有 ${this.car} 辆的车!`);
}
teji() {
console.log("特技:我会一瞬间花 100万!");
}
sing() {
console.log("我会唱歌!!-----------------------");
}
}
class My extends Other {
// 自己构造器函数:
constructor(x, y, z) {
// 一定
super(x, y);
// 1. 给被继承 类 构造函数 确定留几个位置;或者不预留位置;但是必须要调用 super();
// 2. super 必须在this前面调用;
this.z = z;
}
}
var m1 = new My(10, 20, 30);
console.log(m1);
【场景2:在自己类下方法调用 继承过来 类的方法 super,(有可能会出现同名)】
10-小结
- day01:为什么学构造函数,能够更合理创建一个对象;
- day02: 为什么学继承?
- 别人:写好一些构造函数 +原型对象;
- 我们:非常想要!继承过来!
- 把你的属性 call 过来
- 把你的原型对象 想办法 串到我的原型对象;大家形成一个新的原型链;
- ES6:
- 基础语法;
- 记住 super用 场景1 重点!
第三天 有点重要 涉及到 this指向 闭包 递归 函数自己封装深浅拷贝
函数
定义
-
方式1 函数声明方式 function 关键字 (命名函数)
function fn(){}
-
方式2 函数表达式(匿名函数)
var fn = function(){}
-
方式3 new Function()
var f = new Function(‘a’, ‘b’, ‘console.log(a + b)’);
f(1, 2);var fn = new Function(‘参数1’,‘参数2’…, ‘函数体’)
注意
/*Function 里面参数都必须是字符串格式
第三种方式执行效率低,也不方便书写,因此较少使用
所有函数都是 Function 的实例(对象)
*/
调用
/* 1. 普通函数 */
function fn() {
console.log('人生的巅峰');
}
fn();
/* 2. 对象的方法 */
var o = {
sayHi: function() {
console.log('人生的巅峰');
}
}
o.sayHi();
/* 3. 构造函数*/
function Star() {};
new Star();
/* 4. 绑定事件函数*/
btn.onclick = function() {}; // 点击了按钮就可以调用这个函数 什么时候调用?用户点击的时候调用
/* 5. 定时器函数*/
setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次
/* 6. 立即执行函数(自调用函数)*/
(function() {
// console.log('人生的巅峰');
})();
this
函数内部的this指向:这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同;指向我们的调用者.
改变this指向-01-call
应用场景: 经常做继承.
// 语法:函数.call(); 算函数调用方式,配合call;
// 作用:函数在该次的调用的时候,其函数内部this指向 我们指定参数1的那个对象;
// 这个函数要执行的!
// 参数:参数1:函数fn内属性名即将要去到这个参数上;
//
function fn() {
this.a = 10;
this.b = 20;
}
// fn(); // this--->window
// new fn(); // this--->返回的那个实例化对象
var obj = {
info: "我是obj内属性"
};
fn.call(obj); // this---->参数1;
// console.log(obj.a, obj.b);
// fn.call(window); 相当于 fn();
// 延伸:
var btn = document.querySelector("button");
fn.call(btn);
console.log(btn.a, btn.b);
改变this指向-02-apply
这个和call非常像 call第二参数需要用都好隔开, 但是apply第二参数是个数组!
// apply
// 作用:1. 函数在该次的调用的时候,其函数内部this 指向 我们指定参数的那个对象;
// 2. 这个函数要执行的!
// 参数:1. this 指向 我们指定参数的那个对象
// 2. fn要执行的时候,把参数 作为一个数组传入;
function fn(a, b, c, d, e) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
}
var obj1 = {
info: "我是obj1上属性"
};
fn.call(obj1, 10, 20, 30, 40, 50);
console.log(obj1);
var obj2 = {
info: "我是obj2上属性"
}
fn.apply(obj2, [10, 20, 30, 40, 50]);
console.log(obj2);
// 场景:需求 求 一堆数的最大值; 参数:只能逗号分隔传入,比如 现在想求一个数组的中最大值;
// 发现 让把形参形式 从逗号形式 需要转换为 数组形成 进行传入的话,需要用apply 第二个参数功能;
var arr = [15, 18, 99, 45, 74, 56, 999];
// 常规:逗号分隔;上面的数组需要让下面方法求最大值,大家只能手动把数组中每一项输入到下面()内才能得到结果;
var max = Math.max(15, 18, 99, 45, 74, 56, 999);
console.log(max);
// 需求:现在不想手动输入,不想使用for循环,只能把 逗号分隔形式 转化为 数组形式传入;
var max = Math.max.apply({}, arr)
console.log(max);
改变this指向-03-bind
// bind
// 作用:1.改变某个函数内部this指向!
// 2.函数是不会被执行的!返回新函数,内部this已经为固定!
// 参数 :本次代码执行后,函数内this的新的指向,
// 返回值:返回全新的函数 内部this已经 被改变后的 函数!
-
轮播图右侧点击的修改:
MySwiper122.prototype.rClick = function() {
// -------------------------------------------------------实际写法 this.right.onclick = function() { this.index++; if (this.index == 6) { this.index = 0; } // 设置切换 this.ul.style.left = -this.index * this.w + "px"; }.bind(this); // 实例化对象 // 执行过程:把 day-01 临时代替that要好! // 1.发现 赋值号 // 2. 先执行右侧,把右侧结果 赋值给左侧! // 右侧:函数A.bind(); 函数A不执行!函数A.bind()会执行返回一个新的函数!就是右侧的结果! // 左侧:this.right.onclick 给 onclick位置 设置一个新的函数 (内部的this 已经被固定了!)
};
改变this指向-三个方法异同
-
函数遇见call、apply、bind:本次代码中,函数内部的this会指向为 遇见第一参数!
-
面试题:
- !相同点:都是可以改变函数内部this指向!
- !不同点:
- call 和 apply 会调用执行函数!bind 不会执行函数!
- call 和 apply传递的参数不一样, call传递参数使用逗号隔开,apply使用数组传递;
-
应用场景:
- call ES5构造函数的实例属性做继承.
- apply :参数形式 逗号形式---------------------->数组形式
var res = Math.max.apply(Math, [10, 10, 50, 99]) - bind :不执行函数!如果我们只是想改变下函数内部this指向!不让函数执行!
闭包-定义-作用
函数内部:声明一个变量,局部变量。函数执行完成后,变量不能被其他地方使用,垃圾回收机制处理局部变量存储空间,重新释放空间;
闭包:局部变量和函数绑定在一起了!把这个变量隐藏在函数上;意味局部变量不会被内存垃圾回收机制进行回收!
大白话: 外边还能间接使用函数内部的变量,无行中我们隐藏了一个变量
表现:
-
必须是函数a套着函数b
-
函数b内部使用了函数a的局部变量
-
结果 : 使用的局部变量不会被回收, 这个变量和函数b隐藏绑定在一起了
// 函数内部: 声明一个变量,局部变量。函数执行完成后,变量不能被其他地方使用,
// 垃圾回收机制处理局部变量存储空间,重新释放空间;
// 特点:变量不能被其他地方使用// 闭包:基本形式;
// 1.函数套着函数;上午:里面的函数 作为 外面函数 返回值;
// 2.里面的函数 用到 外面函数 的局部变量;// 闭包:局部变量和函数绑定在一起了!把这个变量隐藏在函数上;意味局部变量不会被内存垃圾回收机制进行回收!
function demo() {
var a = 10;return function() { a++; console.log(a); }
}
var fn = demo(); // 接受个函数体;绑定了一个隐藏变量a;
fn(); // a++; console.log(a); 不能回收的a: 11;
fn(); // 找那个不能回收那个a: 现在11; a++; console.log(a);
fn(); // 找那个不能回收那个a: 现在12; a++; console.log(a);// 特点:给一个函数 隐藏一个一直要使用变量,变量不会被回收!
// 这种变量只能是通过函数调用的方式才能获取,咱们直接获取的话是不能获取!
闭包-案例-如何使用
// ------------------------------------------------------案例
// 需求:分别点击每个按钮,在控制台输出对应的下标;webapi
var btns = document.querySelectorAll("button");
for (var i = 0; i < btns.length; i++) {
(function(index) { // 内部形参:局部变量
btns[index].onclick = function() {
console.log(index);
}
})(i); // 实参
}
// 1.执行三次:每次都会有个立即执行函数执行;
// 2.立即执行:内部 btns[index].onclick = function() {} 给每个按钮注册事件,不是执行后面函数;
// 但是随着三次循环,构成3次闭包;
// 3.用户点击的时候: console.log(index); 找和这个函数绑定在一起那个隐藏变量;
递归-1-基本使用
感觉: 函数内部代码的时候 发现细节的地方 需要把整个函数内部内容重写一遍的话, 就是递归!
定义
- 递归:如果一个函数在其内部可以调用其本身,那么这个函数就是递归函数。
- 简单理解: 函数内部自己调用自己;
- 注意: 递归函数的作用和循环效果一样,执行过程不一样;由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
使用
- 递归:基本形式就是自己调用自己,什么时候用呢?
-
函数内部:实现功能的基本流程;
-
在写基本流程的时候,发现有的地方需要再次把 本函数的基本功能需要再次写一次的时候,递归!
-
注意,递归会一直调用自己的;需要退出条件控制;
// -------------------------------------执行过程;
// function fn2() {
// console.log(“我是fn2函数开始”);
// console.log("-----");
// console.log(“我是fn2函数结束”);
// }// function fn1() {
// console.log(“我是fn1函数开始”);
// fn2();
// console.log(“我是fn1函数结束”);
// }// fn1();
// 执行过程:fn2没有执行完 没有遇见 },fn1算没有执行完 没有};// --------------------------------------递归:
// 函数:在内部调用自己;
// 得有退出条件;栈溢出报错;// function fn() {
// console.log(“fn函数的开始”);
// fn();
// console.log(“fn函数的结束”);
// }
// fn();
// 如果没有退出条件,上一次函数在下一次函数没有执行完时,上一次不算执行完;// 和死循环有点不一样:每一次fn都能执行完;
// function fn() {
// console.log(“fn函数的开始”);
// console.log(“fn函数的结束”);
// }
// while (true) {
// fn();
// }
-
递归-2-深浅拷贝
// 笔试:非常大几率;
function copy(_old) {
// _old:接受准备要复制的数据,作为形参很合适;
var _new; // 新的复制出来的数据;
// 1.判断是否我数组
if (_old instanceof Array) {
_new = [];
for (var i = 0; i < _old.length; i++) {
// _old[i]
_new[i] = copy(_old[i]);
}
}
// 2.如果是个对象
else if (_old instanceof Object) {
_new = {};
for (var key in _old) {
// key:"a" _old[key]:10
// key:"b" _old[key]:99
// key:"c" _old[key]:[66,77,88] 不能直接给
// key:"d" _old[key]:{info:"字符串"}
// 思路:对 _old[key] 做一个数据类型判断,及作出不同动作;
// if 数组
// else if 对象
// else 简单
// 感觉:函数内部代码的时候,发现细节的地方 需要把整个函数内容重新需要写一次的话,就是递归!
_new[key] = copy(_old[key]); // 值如果是简单数据类
}
}
// 3.不用判断:简单
else {
_new = _old;
}
return _new;
}
// JQ:$.extend() 查询 可以深度;
// $.clone(); DOM节点
第四天 ECMAScript 6 介绍 ES6
01-let
- let 定义变量,变量不可以再次定义,但可以改变其值;
- 具有块级作用域
- 没有变量提升,必须先定义再使用
- let声明的变量不会压到window对象中,是独立的
如果使用var声明了变量,也不能再次用let声明了,反之也是不行的。
原因也是这个变量已经被声明过了。
不过这只是一种特殊情况了,实际开发要么全部使用var,要么全部使用let。
// ************************************let
// 【1.let 声明变量,不能再次声明了!】
// let a = "abc";
// console.log(a);
// // let a = 10;
// a = 10;
// console.log(a);
// 【2. 块级作用域 { } 块级作用域】 不会影响其他地方变量的值!
// {
// let age = 18;
// console.log(age); // 18
// }
// console.log(age);
// for (let index = 0; index < 3; index++) {
// console.log(index);
// }
// 【3.没有变量提升,必须先定义再使用!】
// let a = 10;
// console.log(a);
// 【4.let声明的变量不会属性window对象下属性,是独立的】
// let info = 10;
// console.log(window.info); // 报错?!不会!undefined
02-const
-
使用const关键字定义常量
-
常量是不可变的,一旦定义,则不能修改其值
-
初始化常量时,必须给初始值
-
具有块级作用域
-
没有变量提升,必须先定义再使用
-
常量也是独立的,定义后不会压入到window对象中
// 声明 常量 :
// 一开始就定下值是多少!
// 后面也不能变!// 封装函数的时候:写配置项:常量
// 常量:不能在改变!变量名(常量名字一般是全部大写)
// const PI = 3.1415926;// 1.常量 不能被重新赋值!
// PI = 3.14;// 2.初始化的时候,const 变量 = 必须给初始化的值!
// const PI;
// PI = 3.1415926; //报错!// 3. 具有块级作用域
// 4. 没有变量提升,必须先定义再使用
// 5. 常量也是独立的,定义后不会压入到window对象中
解构赋值
注意: 怎么区分 展开语法和解构赋值 看…后面的自定义名字有没有值, 有值就是展开语法,没值就是解构赋值
ES6 允许按照一定 模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
数组的解构
方便获取数组中的某些项
解构:解剖;意义:从数组或者对象快速拿到你需要的属性值!
// 解构基本使用:
// let [a, b, c] = arr;
// console.log(a, b, c);
// 其他
// 1.变量多,值少
// let [a, b, c, d] = arr;
// console.log(a, b, c, d); // d 准备那数组中不存在一项 undefined!
// 2.变量少,值多
// let [a, b] = arr;
// console.log(a, b); // 按照顺序解构赋值!
// 3.就想获取其中某些位置的值!按需取值的!
// 不需要用变量接收的值,用空位占位;
// let arr = [5, 9, 10, 8, 3, 2];
// let [, , a, , b] = arr; // 不需要的位置不需要用变量接收的值,用空位占位
// console.log(a, b);
// 4. 剩余值:...
// let arr = [5, 9, 10, 8, 3, 2];
// let [a, b, ...c] = arr; // ... 剩下全部都被c 结构出来!
// console.log(a, b, c);
// // 快速复制数组!
// let [...newArr] = arr;
// console.log(newArr == arr);
// 5. 复杂:数组套着数组
// let arr = ['zhangsan', 18, ['175cm', '65kg']];
// let [ , ,[a, b]] = arr; // !对应形式!
// console.log(a, b);
对象的解构
方便解析对象中的某些属性的值
意义:模仿形式,点上获取数据;数组:位置,不讲究名字;对象:名字,不讲究位置!
// 数组:讲究是位置一一对应的位置
// 对象:属性名,位置上名字,没有顺序问题!
// 对象解构基本使用:默认要求变量名和属性名一样!
// let { bar,foo } = {foo: 'aaa',bar: 'bbb'};
// console.log(foo,bar);
// 其他
// 1. 需要换个新的变量名 人性化
// let { a, b:c } = { a: 'hello', b: 'world' };
// console.log(a,c);
// 2. 获取其中一个属性值!对名字单独写!
// let { a:res } = { a: 'hello', b: 'world' };
// console.log(res);
// 3. 剩余值:...
// let obj = { name: 'zs', age: 20, gender: '男' };
// // let {name,...a} = obj;
// // console.log(a);
// // 拷贝对象:
// let {...newObj} = obj;
// 4.复杂情况:对形式!对名字!
// let obj = {
// name: 'zhangsan',
// age: 22,
// dog: {
// name: '毛毛',
// age: 3
// }
// };
// let {
// dog: {
// name,
// age: a
// }
// } = obj;
// console.log(name, a);
箭头函数-基本形式
-
ES6 中允许使用箭头定义函数 (=> goes to),目的是简化函数的定义并且里面的this也比较特殊。
-
箭头函数的基本定义
// -----------------------------------------------------------------1.基本使用
// 函数:传统的方式
// let fn = function(x) {
// return x * x;
// }// 1.简化为箭头函数: // let fn = (x) => { // return x * x; // } // let res = fn(10); // console.log(res); // 2.形参 如果形参只有一个;()可以省略 // let fn = x => { // return x * x; // } // 3.函数体 如果只有一句话;{}可以省略 且返回这句话; // let fn = (x, y) => x * y; // let res = fn(2, 3); // console.log(res); //特点3箭头函数内部没有 arguments //let fn = () => { // console.log(arguments); // 报错,arguments is not defined // }; // fn(1, 2);
箭头函数-this
箭头函数很特别!
箭头函数内部的 this 指向外部作用域中的 this
-
箭头函数没有自己的 this
-
如果箭头函数内部使用了 this ,那么在定义函数的时候,就已经确定了它的指向,而非运行阶段
-
定义箭头函数时,按照作用域去查找这个this即可,即可确定this指向什么
-
箭头函数:内没有自己的this; 在其内部用到this,肯定上一层作用域this; 直到找到全局的this:window;
-
function声明的函数:内部this规则 谁调用就是谁!
// 注意:1.箭头函数不能当做构造函数使用(内部没有this)
// 2.箭头不能使用 call apply bind(内部没有this)// ********************************************************this指向
// 形式:function (){} this: 谁调用是谁!函数被调用被执行的时候! 【调用时候】
// 形式:箭头函数 ()=>{} this:
// 本身没有this,
// 用的别人的this!
// 箭头函数在定义设置的时候,按照作用域往上去查找这个this// ********调用时候 // let obj = { // fn: function() { // console.log(this); // } // } // obj.fn(); // *********定义时候 // let obj = { // fn: () => { // console.log(this); // } // } // obj.fn(); let obj = { fn1: function() { setTimeout(() => { console.log(this); // obj }, 0); }, fn2: () => { setTimeout(() => { console.log(this); // window }, 0); }, fn3: function() { setTimeout(function() { console.log(this); // window }, 0); } }
MySwiper122.prototype.rClick = function() {
// -------------------------------------------------------实际写法 this.right.onclick = () => { // 原来this: DOM节点 this.index++; if (this.index == 6) { this.index = 0; } // 设置切换 this.ul.style.left = -this.index * this.w + "px"; };
};
箭头函数-形参默认值
ES6 之前函数不能设置参数的默认值
// ------------------------------------------------------------现在:
// 真的传入实参的值?不是调用!只是在设置默认值!
function fn(name = "ls", age = 10) {
console.log(name, age);
}
fn(20, 30);
let aa = (name = "ls", age = 10) => {
console.log(name, age);
}
aa("--------------");
-----------------------------------------------------------
// ES5 中给参数设置默认值的变通做法
function fn(x, y) {
y = y || 'world';
console.log(x, y);
}
fn(1)
// ES6 中给函数设置默认值
function fn(x, y = 'world') {
console.log(x, y);
}
fn(2)
fn(2,3)
箭头函数-rest参数
rest 参数:剩余参数,以 … 修饰最后一个参数,把多余的参数都放到一个数组中。可以替代 arguments 的使用
注意:rest 参数只能是最后一个参数
// 监听函数内部没有arguments 内部变量!arguments:解决的形参个数不确定的时候!
//
// 【新知识:剩余参数,rest参数】
// 基本语法:(...自己设置的形参名字)
// 得到:形参个数的数组!
// let fn = (...a) => {
// console.log(a);【理解:... 看为 1,2,88,99,77】
// }
// 其他使用理解:和数组解构很像!箭头函数同样适用!
function fn(a, b, ...c) {
console.log(a, b, c);
}
fn(10, 50, 88, 99, 77, 55);
展开语法 注意有点像解构赋值但是不是
注意: 怎么区分 展开语法和解构赋值 看…后面的自定义名字有没有值, 有值就是展开语法,没值就是解构赋值
- … 展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;
还可以在构造字面量对象时, 将对象表达式按key-value的方式展开
// 展开语法:...
// ************************************场景1
// 参数:逗号隔开,自己输入!
// 曾经想法:数据逗号形式------->数组形式! apply
// var arr = [1, 5, 8, 99, 56, 78];
// Math.max.apply(null, arr);
// 现在:... 帮你把数组展开为 ------------>逗号分隔!
// var res = Math.max(...arr); // [1, 5, 8, 99, 56, 78] ------------>1, 5, 8, 99, 56, 78
// console.log(res);
// ************************************场景2
//
// let arr1 = [1, 2, 3];
// let arr2 = [4, 5, 6];
// 拼接为一个新的数组
// 曾经:let res = arr1.concat(arr2);
// 现在:再新的数组展开
// let arr3 = [...arr1, ...arr2];
// ************************************场景3:把对象属性展开到另外一个对象上
// 对象 复制对象:潜拷贝
let obj = {
id: 1,
info: "xxxxx"
};
let obj_1 = {
...obj,
// name: "zs",
// age: 18,
}
console.log(obj_1 == obj);
// --------------------对象解构
// let obj = { name: 'zs', age: 20, gender: '男' };
// // let {name,...a} = obj;
// // console.log(a);
// // 拷贝对象:
// let {...newObj} = obj;
内置对象的扩展 【自学】
Array 的扩展
- Array.from()
-
把伪数组转换成数组
-
伪数组必须有length属性,没有length将得到一个空数组
-
转换后的数组长度,是根据伪数组的length决定的
let fakeArr = {
0: ‘a’,
1: ‘b’,
2: ‘c’,
length: 3
};let arr = Array.from(fakeArr);
console.log(arr); // [‘a’, ‘b’, ‘c’]// 转数组的对象必须有length值,因为得到的数组的成员个数是length指定的个数
// 上例中,如果length为2,则得到的数组为 [‘a’, ‘b’]
-
- forEach遍历数组
-
要为forEach传递一个函数进来
-
为forEach传递的函数有三个形参,分别表示数组的值、下标、当前的数组
// [xxx,xxx].forEach(function (value, index, arr) {
// value 表示数组的值
// index 表示数组的下标、索引
// arr 表示当前的数组
// });[3, 8, 4, 9].forEach(function (v, i, a) { console.log(v); // 表示数组的值 ,输出的结果是 3,8,4,9 // console.log(i); // 表示数组的下标 // console.log(a); // 表示数组 }); // 如果不需要下标和当前的数组,只使用value即可 // 函数可以使用箭头函数 [3, 8, 4, 9].forEach((item) => { console.log(item); }); // 下面的意思是循环,在循环数组的时候,输出数组的每个值 [3, 8, 4, 9].forEach(item => console.log(item));
-
- 数组实例的 find() 和 findIndex()
- find和findIndex方法,会遍历传递进来的数组
- 回调函数有三个参数,分别表示数组的值、索引及整个数组
- 回调函数中return的是一个条件,find和findIndex方法的返回值就是满足这个条件的第一个元素或索引
- find 找到数组中第一个满足条件的成员并返回该成员,如果找不到返回undefined。
- findIndex 找到数组中第一个满足条件的成员并返回该成员的索引,如果找不到返回 -1。
// 语法结构
let arr = [1, 2, 4, 0, -4, 3, -2, 9];
arr.find(function (item, index, self) {
console.log(item); // 数组中的每个值
console.log(index); // 数组中的每个索引/下标
console.log(self); // 当前的数组
});
// 用法:找数组中第一个小于0的数字
let arr = [1, 2, 4, 0, -4, 3, -2, 9];
let result = arr.find(function (item) {
return item < 0; //遍历过程中,根据这个条件去查找
});
console.log(result); // -4
findIndex 的使用和 find 类似,只不过它查找的不是值,而是下标
- 语法类似的方法 : some/every/map/forEach/filter/reduce
- 数组实例的 includes()
- 判断数组是否包含某个值,返回 true / false
- 参数1,必须,表示查找的内容
- 参数2,可选,表示开始查找的位置,0表示开头的位置
let arr = [1, 4, 3, 9];
console.log(arr.includes(4)); // true
console.log(arr.includes(4, 2)); // false, 从2的位置开始查,所以没有找到4
console.log(arr.includes(5)); // false
String的扩展
-
includes(), startsWith(), endsWith()
includes(str, [position]) 返回布尔值,表示是否找到了参数字符串 -
startsWidth(str, [position]) 返回布尔值,表示参数字符串是否在原字符串的头部或指定位置
-
endsWith(str, [length]) 返回布尔值,先截取length个长度的字符,然后判断结尾是什么。
console.log(‘hello world’.includes(‘e’, 2)); // false 从位置2开始查找e,没有找到
console.log(‘hello world’.includes(‘e’)); // trueconsole.log(‘hello world’.startsWith(‘h’)); // 未指定位置,看开头是否是h,返回true
console.log(‘hello world’.startsWith(‘l’, 2)); // 指定位置的字符是l,返回trueconsole.log(‘hello world’.endsWith(‘d’)); // 未指定位置,结尾是d,返回true
console.log(‘hello world’.endsWith(‘o’, 5)); // 先截取5个字符,即’hello’,判断hello的结尾是否为 ‘o’,返回true -
repeat()
repeat方法返回一个新字符串,表示将原字符串重复n次。
let html = ‘ - itheima
- ’;
html = html.repeat(10); -
trim()trim() 方法可以去掉字符串两边的空白
console.log(’ hello ‘.trim()); // hello
console.log(’ hello '); // hello
Number的扩展
- ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,功能完全保持不变。
- Number.parseInt()
- Number.parseFloat()
console.log(parseInt(‘123abc’));
// ES6中,将parseInt移植到了Number对象上
console.log(Number.parseInt(‘123abc’));
Set 一般应用到去重
一般应用到去重
-
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
-
Set本身是一个构造函数,用来生成 Set 数据结构。
// 1. 基本使用
let s = new Set();
// 得到一个空的Set对象
// 调用add方法,向s中添加几个值
s.add(3);
s.add(7);
s.add(9);
s.add(7); // Set对象中的成员都是唯一的,前面添加过7了,所以这里添加无效console.log(s.size);
console.log(s); // {3, 7, 9} -
Set 的API
-
size:属性,获取 set 中成员的个数,相当于数组中的 length
-
add(value):添加某个值,返回 Set 结构本身。
-
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
-
has(value):返回一个布尔值,表示该值是否为Set的成员。
-
clear():清除所有成员,没有返回值。
// 将一些重复的值加入到Set对象中,看看效果
const s = new Set();// 使用forEach遍历前面的数组,然后将数组中的每个值都通过Set对象的add方法添加到Set对象中
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
// s = {2, 3, 5, 4}
-
-
另外初始化Set的时候,也可以为其传入数组或字符串,得到的Set对象中的成员不会有重复。根据这个特点可以完成数组或字符串去重。
// ---------------------------------------------------1.基本用法
// let s = new Set();
// // 方法:
// s.add(3);
// s.add(99);
// s.add(88);
// s.add(99);
// s.add(3);// console.log(s.size); // s数据:包含不重复的数据 // console.log(s); // -----------------------------------------------------2.应用 // 1.用法; // let arr = [99, 99, 10, 20]; // let s = new Set(); // for (let i = 0; i < arr.length; i++) { // s.add(arr[i]) // } // arr = [...s]; // console.log(arr); // 2.用法: let arr = [99, 99, 10, 20]; let s = new Set(arr); arr = [...s]; // 简化: arr = [...new Set(arr)]; // ES5: var new_arr = []; for (let i = 0; i < arr.length; i++) { // 新数组里面没有 arr[i] 这个值,新的值; if (new_arr.indexOf(arr[i]) == -1) { new_arr.push(arr[i]); } }
-
for
// 遍历Set对象,发现重复的值只有一份
// for…in 循环中的 i 表示数组的下标,或对象的属性名
// for…of 循环中的 i 表示数组的值,或对象的值
for (let i of s) {
console.log(i);
}
// 2 3 5 4
定义对象的简洁方式
let id = 1;
let name = 'zs';
let age = 20;
// 之前定义对象的方案
// let obj = {
// // 属性: 值
// id: id,
// name: name,
// age: age,
// fn: function () {
// console.log(this.age);
// }
// };
// obj.fn();
// ES6的新方案
let obj = {
id, // 属性名id和前面的变量id名字相同,则可以省略 :id
name,
nianling: age,
// 下面的函数是上面函数的简化写法,可以省略 :function 。但是注意这里仅仅是上面函数的简化,不是箭头函数
fn () {
console.log(name);
}
};
obj.fn();
正则
介绍
- 作用:正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式
- 是什么:正则表达式也是对象。
- 场景:正则表通常被用来检索、替换那些符合某个模式(规则)的文本,
- 验证:例如表单,用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。
- 过滤:正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。
- 要求
- 灵活性、逻辑性和功能性非常的强。可以迅速地用极简单的方式达到字符串的复杂控制。
- 对于刚接触的人来说,比较晦涩难懂。/^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$/
- 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /1{3,16}$/
语法
-
对象:{} [] 函数 DOM BOM 用户行为event 正则
-
方式一:通过调用RegExp对象的构造函数创建
var regexp = new RegExp(/123/);
console.log(regexp); -
方式二:利用字面量创建 正则表达式; 正则对象和字母类字符串没有任何关系!
var rg = /规则内容/;
test方法
-
test() 正则对象的方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。
var rg = /abc/;
console.log(rg.test(123));
console.log(rg.test(‘abc’));
特殊字符
- 一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。
- 参考:
- 特殊字符非常多,可以参考: MDN
- jQuery 手册:正则表达式部分 正则测试工具
边界符
-
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
边界符 说明
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束) -
如果 ^和 $ 在一起,表示必须是精确匹配。
-
注意:正则表达式里面不需要加引号 不管是数字型还是字符串型
var rg = /abc/;
// /abc/ 只要包含有abc这个字符串返回的都是true
console.log(rg.test(‘abc’));
console.log(rg.test(‘abcd’));
console.log(rg.test(‘aabcd’));
console.log(’---------------------------’);
var reg = /^abc/;
console.log(reg.test(‘abc’)); // true
console.log(reg.test(‘abcd’)); // true
console.log(reg.test(‘aabcd’)); // false
console.log(’---------------------------’);
var reg1 = /^abc$/; // 精确匹配 要求必须是 abc,而且只能是 abc 3个字符的 字符串才符合规范
console.log(reg1.test(‘abc’)); // true
console.log(reg1.test(‘abcd’)); // false
console.log(reg1.test(‘aabcd’)); // false
console.log(reg1.test(‘abcabc’)); // false
( | )
-
从()内选出一组就可以
var rg = /^(abc|bbb|ccc)$/;
rg.test(“bbb”) // true;
[]
-
表示有一系列字符可供选择,只要匹配其中一个就可以了
var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true
console.log(rg.test(‘andy’));//true
console.log(rg.test(‘baby’));//true
console.log(rg.test(‘color’));//true
console.log(rg.test(‘red’));//false
var rg1 = /2$/; // 三选一 只有是a 或者是 b 或者是c 这三个字母才返回 true
console.log(rg1.test(‘aa’)); //false
console.log(rg1.test(‘a’));//true
console.log(rg1.test(‘b’));//true
console.log(rg1.test(‘c’));//true
console.log(rg1.test(‘abc’));//falsevar reg = /3$/ //26个英文字母任何一个字母返回 true - 表示的是a 到z 的范围
console.log(reg.test(‘a’));//true
console.log(reg.test(‘z’));//true
console.log(reg.test(‘A’));//false//字符组合
var reg1 = /4$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true//取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。
var reg2 = /[a-zA-Z0-9]$/;console.log(reg2.test(‘a’));//false
console.log(reg2.test(‘B’));//false
console.log(reg2.test(8));//false
console.log(reg2.test(’!’));//true
量词符
-
量词符用来设定某个模式出现的次数。
量词 说明
-
重复0次或更多次
-
重复1次或更多次
? 重复0次或1次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次 -
-
案例:用户名表单验证功能需求:
- 如果用户名输入合法, 则后面提示信息为: 用户名合法,并且颜色为绿色
- 如果用户名输入不合法, 则后面提示信息为: 用户名不符合规范, 并且颜色为红色
-
分析:
- 用户名只能为英文字母,数字,下划线或者短横线组成, 并且用户名长度为6~16位.
- 首先准备好这种正则表达式模式 /5{6,16}$/
- 当表单失去焦点就开始验证:
- 如果符合正则规范, 则让后面的span标签添加 right类.
- 如果不符合正则规范, 则让后面的span标签添加 wrong类.
-
代码:
请输入用户名
-
括号总结
- 大括号 {}量词符. 里面表示重复次数
- 中括号 字符集合。匹配方括号中的任意字符.
- 小括号 表示运算的优先级:JS
- 正则表达式在线测试
预定义
-
预定义类指的是某些常见模式的简写方式.
-
案例:验证座机号码
var reg = /^(\d{3}-\d{8}|\d{4}-\d{7})$/;
-
案例:表单验证案例
// 手机号验证:/^1[34578][0-9]{9}$/;
//验证通过与不通过更换元素的类名与元素中的内容
if (reg.test(this.value)) {
// console.log(‘正确的’);
this.nextElementSibling.className = ‘success’;
this.nextElementSibling.innerHTML = ’ 恭喜您输入正确’;
} else {
// console.log(‘不正确’);
this.nextElementSibling.className = ‘error’;
this.nextElementSibling.innerHTML = '格式不正确,请从新输入 ';
}//QQ号验证: /6\d{4,}KaTeX parse error: Can't use function '\u' in math mode at position 18: … //昵称验证: /^[\̲u̲4e00-\u9fa5]{2,…/
// “\u4e00”和“\u9fa5”是unicode编码,并且正好是中文编码的开始和结束的两个值,
/ 所以这个正则表达式可以用来判断字符串中是否包含中文。//验证通过与不通过更换元素的类名与元素中的内容 ,将上一步的匹配代码进行封装,多次调用即可
function regexp(ele, reg) {
ele.onblur = function() {
if (reg.test(this.value)) {
// console.log(‘正确的’);
this.nextElementSibling.className = ‘success’;
this.nextElementSibling.innerHTML = ’ 恭喜您输入正确’;
} else {
// console.log(‘不正确’);
this.nextElementSibling.className = ‘error’;
this.nextElementSibling.innerHTML = ’ 格式不正确,请从新输入 ';
}
}
};//密码验证:/7{6,16}$/
//再次输入密码只需匹配与上次输入的密码值 是否一致
字符串.replace
-
字符串的方法:replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
var str = ‘andy和red’;
var newStr = str.replace(‘andy’, ‘baby’);
console.log(newStr)//baby和red// 等同于 此处的andy可以写在正则表达式内
var newStr2 = str.replace(/andy/, ‘baby’);
console.log(newStr2)//baby和red// 替换一个
var str = ‘abcabc’
var nStr = str.replace(/a/,‘哈哈’)
console.log(nStr) //哈哈bcabc// 全部替换 g
var nStr = str.replace(/a/g,‘哈哈’)
console.log(nStr) //哈哈bc哈哈bc// 忽略大小写i
var str = ‘AAbcAba’;
var newStr = str.replace(/a/i,‘哈哈’)//“哈哈AbcAba”// 忽略大小写i且 全部替换
var str = ‘AAbcAba’;
var newStr = str.replace(/a/ig,‘哈哈’)//“哈哈AbcAba” -
案例:过滤敏感词汇
提交
老师笔记的正则代码
正则-介绍-使用
// 正则:是对象;
// 语法: /中间写规则/ 遇到字母,不要加单双引号
// 对象:方法和属性集合体;
var reg = /123/; // 只要包含123就可以
// 方法:正则.test();
// 测试:参数数字和或者字符串,是否满足正则的要求;如果满足,方法返回true;
// 效果:获取用户输入的信息,是否满足正则!
var res = reg.test(112333);
console.log(res);
正则-边界符
// 字母:写abc不要写单双引号;
// ---------------------------------------- ^:必须以我们写的规则的文字开头:
//var rg = /^abc/;
// var res = rg.test("abcd"); true
//var res = rg.test("aabcd"); //false
// ----------------------------------------- $:必须以我们写的规则的文字结尾
var rg = /abc$/;
// console.log(rg.test("abcd")); // false
// console.log(rg.test("ababc")); // true
// --------------------------------------- 合起来用;必须以规则,必须以规则;只能是规则本身!
var rg = /^abc$/; // 必须以abc开头,以abc结尾;只能是abc这个三个字符;只能出现一次;
var res = rg.test("abcabc"); // false
console.log(res);
正则-小括号
// -----------------------------------------( | ) 从()内选出一个组
// var rg = /^(abc|bbb|ccc)$/;
// console.log(rg.test("abc")); // true
// console.log(rg.test("abcbbbccc")); // false
var reg = /^(abc|aaa|bbb)(abc|aaa|bbb)$/;
console.log(reg.test("aaabbb")); // true
console.log(reg.test("aaa")); // false;
正则-中括号
// []: 1.和前面数组没有关系 2.从[]选择出一个字符;3.可以多次使用
// 范围:
// a-zA-Z0-9
// [^a-zA-Z0-9] 除去这个范围的取值;
// var reg = /^[abcde]$/;
// console.log(reg.test("a")); // true
// console.log(reg.test("aa")); // false
// var reg = /[abcde]/; // 不是严格要求,只要包含就OK
// console.log(reg.test("afff")); // true
// 26个字母:小写
// var reg = /^[a-z]$/;
// console.log(reg.test("123456a"));
// 26个字母:大写
// var reg = /[A-Z]/;
// console.log(reg.test("abc")); //false
// 数字 0-9
// var reg = /^[0-9]$/;
// console.log(reg.test("123abc")); //false
// 组合:
// var reg = /[a-zA-Z0-9]/;
// console.log(reg.test("中国")); // false
// 在[^ ] 取对立面
var reg = /[^a-zA-Z0-9]/; //只要包含对立面就OK
console.log(reg.test("中国a")); // true;
var reg = /^[^a-zA-Z0-9]/;
console.log(reg.test("a中国")); // false;
var reg = /^[^a-zA-Z0-9]$/;
console.log(reg.test("-")); // true;
正则-量词符
// * : 出现的次数 >=0;
// + : 出现的次数 >=1;
// ? : 出现的次数 0 || 1;
// {n} : 必须出现的 n 次数;
// {n,} : 必须出现的 >=n 次数;
// {n,m} : 必须出现的 n~m 次数;
正则-预定义
//
// \d [0-9]
// \w 字母数字_
// 座机号:
// 010-00001111;
// 0352-0001111;
// var reg = /^(\d{3}-\d{8}|\d{4}-\d{7})$/;
// \d{3}-\d{8}
// \d{4}-\d{7}
// 手机号验证:/^1[34578][0-9]{9}$/;
// QQ号验证: /^[1-9]\d{4,}$/;
// 昵称验证: /^[\u4e00-\u9fa5]{2,8}$/
// 不需要:百度查;
正则-replace
-
trim():去除字符串两边的空格;
-
replace:经常用于和正则配合进行文本脱敏
// -------------------------------------------------- // 替换一次 // var str = 'abcabc' // var nStr = str.replace(/a/, '哈哈') // console.log(nStr); // 全部:g 全球 global // var nStr = str.replace(/a/g, '哈哈'); // console.log(nStr); // 忽略大小写 i 忽略 // var str = 'abcAbc' // var nStr = str.replace(/a/gi, '哈哈'); // console.log(nStr);
-
过滤敏感字:
var btn = document.querySelector(“button”);
var textarea = document.querySelector(“textarea”);
var div = document.querySelector(“div”);
btn.onclick = function() {
var nStr = textarea.value.replace(/(激情|gay)/gi, “**”);
div.innerText = nStr;
};