javascript进阶面向对象ES6
文章目录
- es6创建类
- 类中添加共有方法
- 类继承extends和super关键字
- super
- 子类继承父类的方法同时扩展自己的方法
- 类里面的this指向
- 面向对象tab栏
- 构造函数和原型
- 实例成员和静态成员
- 构造函数原型对象prototype
- 对象原型__proto__
- 原型constructor构造函数
- 构造函数、实例和原型对象三角关系
- 原型链
- 对象成员查找规则
- 原型对象this指向
- 利用原型对象扩展内置对象方法
- 继承
- call()
- 利用父构造函数继承属性
- 利用原型对象继承方法
- 类的本质
- 新增的方法
- 迭代(遍历数组)forEach
- filter
- some
- 查询商品案例
- 渲染页面数据
- 根据商品名称筛选商品
- some和forEach的区别
- trim
- Object.defineProperty()定义新属性或者修改原来的属性
- 函数进阶
- 函数的定义和调用
- 函数的调用方式
- 函数内this的指向
- 改变函数内部this指向
- call()
- apply
- bind
- call apply bind总结
- 严格模式
- 严格模式下this指向问题
- 函数变化
- 高阶函数
- 闭包
- 闭包应用
- 点击li打印当前索引号
- 3秒钟后打印
- 计算打车价格
- 思考题
- 递归
- 利用递归求阶乘
- 利用递归求斐波那契
- 利用递归遍历数据
- 浅拷贝和深拷贝
- 深拷贝
- 正则表达式
- 正则表达式在javascript中的使用
- 测试正则表达式 test() 返回true false
- 正则表达式里面的特殊字符
- 边界符
- 字符类
- 量词符
- 量词重复某个模式的次数
- 用户名验证
- 括号总结
- 预定义类
- 座机号码验证
- 表单验证
- 正则替换
面向对象特性:封装性 继承性 多态性
面向对象的思维特点
- 抽取抽象对象共用的属性和行为封装成一个类(模板)
- 对类进行实例化,获取类的对象
万物皆对象,对象是一个具体事物,
对象有属性和方法
类抽象了对象的公共部分,泛指一大类
对象特指某一个,通过类实例化一个具体的对象
es6创建类
constructor()方法是类的构造函数,用于传递参数,返回实例对象
// 1.创建类 class 明星类class Star {constructor(uname, age) {this.uname = unamethis.age = age}
}// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 30)
var zxy = new Star('张学友')
console.log(ldh)
console.log(zxy.uname)
//Star { uname: '刘德华', age: 30 }
//张学友
- 通过class关键字创建类,类名首字母大写
- 类里的constructor函数,可以接收传递过来的参数,同时返回实例对象
- constructor函数只要new生成实例时,就会自动调用这个函数,如果不写这这个函数,类也会自动生成这个函数
- 生成实例 new不能省
- 创建类 类名后不加小括号,生成实例 类名后加小括号,构造函数不需要加function
类中添加共有方法
多个函数方法之间不需要添加逗号分隔
// 1.创建类 class 明星类class Star {constructor(uname, age) {this.uname = unamethis.age = age}sing(song) {console.log(this.uname+'sing'+song)}
}// 2. 利用类创建对象 new
var ldh = new Star('刘德华', 30)
var zxy = new Star('张学友')
console.log(ldh)
console.log(zxy.uname)
ldh.sing('冰雨')
类继承extends和super关键字
class Father {constructor(x, y) {this.x = xthis.y = y}sum() {console.log(this.x + this.y)}money() {console.log(100)}
}class Son extends Father{constructor(x, y) {super(x, y); //调用了父类中的构造函数}
}var son = new Son(1, 3)
var son2 = new Son(11, 3)
son.money() //100
son.sum() //4
son2.sum()//14
super
class Father {say() {return '我是爸爸'}
}class Son extends Father {say() {// console.log('我是二字')console.log(super.say()+'的儿子')// super.say() 调用父类中的普通函数}
}
var son = new Son()
son.say() // 我是爸爸的儿子// 继承中的属性或方法查找原则:就近原则
子类继承父类的方法同时扩展自己的方法
class Father {constructor(x, y) {this.x = x this.y = y}sum() {console.log(this.x + this.y)}
}class Son extends Father {constructor(x, y) {// 利用super调用父类的构造函数// super必须在子类this之前调用super(x, y)this. x = x this.y = y}subtract() {console.log(this.x - this.y)}
}var son = new Son(10, 5)
son.sum() // 15
son.subtract() // 5
使用类的注意点:
在es6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
类里面共有的属性和方法一定要加this使用
类里面的this指向
constructor里面的this指向的是 创建的实例对象
方法里面的this指向这个方法的调用者
<!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><button>点击</button><script>var that;class Star {constructor(uname, age) {// constructor里面的this指向的是 创建的实例对象that = thisthis.uname = unamethis.age = agethis.btn = document.querySelector('button')// 这里的sing是btn调用的 指向btn btn.uname为undefined 可以把this改为thatthis.btn.onclick = this.sing}sing() {// 解决方法 把这里的this改为that// console.log(this.uname)console.log(that.uname)}}var ldh = new Star('刘德华')console.log(that === ldh)</script>
</body>
</html>
面向对象tab栏
功能需求:
- 点击tab栏,可以切换效果
- 点击+号,可以添加tab项和内容项
- 点击x号,可以删除当前的tab项和内容项
- 双击tab项文字或者内容项文字,可以修改里面的内容
抽取对象 tab对象
该对象具有切换功能
该对象具有添加功能
改对象具有删除功能
该对象具有修改功能
添加功能:点击+可以实现添加新的选项卡和内容
5. 创建新的选项卡li 和新的内容section
6. 把创建的两个元素追加到对应的父元素中
以前的做法:动态创建元素createElement
,但是元素里面内容较多,需要innerHTML赋值 ,在appendChild追加到父元素里面
现在高级做法:利用insertAdjacentHTML()
可以直接把字符串格式元素添加到父元素中
删除功能;
点击x可以删除当前的li选项卡和当前的section
x是没有索引号的,但是它的父亲li有索引号,这个索引号正是我们想要的索引号
所以核心思路是: 点击x号可以删除这个索引号对应的li和section
编辑功能:
双击选项卡li或者section里面的文字,可以实现修改功能
双击事件:ondblclick
如果双击文字,会默认选定文字,此时需要双击禁止选中文字
window.getSelection?window.getSelection().removeAllRanges(): document.selectionempty()
核心思路:双击文字的时候,在里面生成一个文本框,当失去焦点或者按下回车 然后把文本框的值给原先的元素即可
代码
构造函数和原型
在es6之前,对象不是基于类创建的,而是构造函数这种特殊函数来定义对象和它们的特征
创建对象三种方式:
- new Objext()创建对象
- 对象字面量
- 构造函数
new在执行时会做四件事 - 在内存中创建一个新的对象
- 让this指向这个新的对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(所以构造函数里面不需要return)
实例成员和静态成员
// 构造函数中的属性和方法称为成员 成员可以添加
function Star(uname, age){this.uname = unamethis.age = agethis.sing = function(){console.log('唱歌')}
}var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',10)
// 实例成员就是构造函数内部通过this添加的成员 uname age sing就是实例成员
// 实例成员只能通过实例化的对象来访问
console.log(ldh.uname)ldh.sing()
console.log(Star.uname) // 不可以通过构造函数来访问实例成员
// 静态成员 在构造函数本身上添加的成员
Star.sex = '男' // 此时sex是静态成员 只能通过构造函数
console.log(ldh.sing() === zxy.sing()) // false
构造函数原型对象prototype
构造函数方法很好用,但是存在浪费内存的问题
我们希望所有的对象使用同一个函数,这样就比较节省内存,但是应该如何做
构造函数通过原型分配的函数是所有对象所共享的
每一个构造函数都有一个prototype对象
我们可以把那些不变的方法,直接定义在prototyoe对象上,这样所有的对象的实例就可以共享这些方法
// 构造函数中的属性和方法称为成员 成员可以添加
function Star(uname, age){this.uname = unamethis.age = age// this.sing = function(){// console.log('唱歌')// }
}
Star.prototype.sing = function() {console.log('我们唱歌')
}var ldh = new Star('刘德华',18)
var zxy = new Star('张学友', 30)
ldh.sing()
zxy.sing()
console.log(ldh.sing() === zxy.sing()) // true
原型是什么:
一个对象,我们也称为prototype为原型对象
原型的作用是什么:
共享方法
一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象上
对象原型__proto__
对象身上系统自动添加一个__proto__指向我们的构造函数的原型对象
console.log(ldh.__proto__ === Star.prototype) // true
方法的查找规则:
说先看ldh实例对象身上是否有sing方法,如果有就执行这个对象上的sing
如果没有,因为__proto__的存在,就去构造函数原型对象prototype身上去查找sing这个方法
原型constructor构造函数
对象原型__proto__和构造函数prototype原型对象里面都有一个属性constructor属性,
constructor称为构造函数,因为它指回构造函数本身
constructor主要用于记录该对象引用与哪个构造函数,他可以让原型对象重新指向原来的构造函数
很多情况下,我们需要手动的利用这个属性指回原来的构造函数
如下
Star.prototype = {//如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数constructor: Star,sing: function() {},movie: function() {}
}
构造函数、实例和原型对象三角关系
原型链
function Star(uname, age) {this.uname = unamethis.age = age
}
Star.prototype.sing = function() {console.log('我会唱歌')
}
var ldh = new Star('ldhk', 33)
// 只要是对象就有__proto__原型 指向原型对象
console.log(Star.prototype)
console.log(Star.prototype.__proto__ === Object.prototype)
// Star原型对象里面的__proto__原型指向的是Object.prototype
console.log(Object.prototype.__proto__)
// Object.prototyp原型对象里面的__proto__原型 指向为null
对象成员查找规则
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
- 如果还没有就查找原型对象的原型(Object的原型对象)
- 依次类推一直找到Object为止(null)
- __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说是一条路线
原型对象this指向
function Star(uname, age) {this.uname = unamethis.age = age
}var that;
Star.prototype.sing = function() {console.log('我会唱歌')that = this
}var ldh = new Star('李德华', 39)
// 1. 在构造函数中,里面的this指向的是对象实例 this
ldh.sing()
console.log(that === ldh) // true// 2.原型对象函数里面的this指向的是实例对象 ldh
// this一般指向调用者
利用原型对象扩展内置对象方法
// 原型对象的应用 扩展内置对象方法
// console.log(Array.prototype)Array.prototype.sum = function() {var sum = 0for(var i = 0; i < this.length; i ++) {sum += this[i]}return sum
}var arr = [1,2,3]
console.log(arr.sum())//6
注意:数组和字符串内置对象不能给原型对象覆盖操作Array.prototype= {}
只能是Array.prototype.xxx = function(){}
的方式
继承
es6之前并没有提供extends继承。可以通过构造函数+原型对象模拟实现继承,被称为组合继承。
call()
调用这个函数,并且修改函数运行时的this指向
fun.call(thisArg,arg1,arg2,,,,)
this.Arg:当前调用函数this的指向对象
function fn() {console.log('我想要喝水')console.log(this)
}
var o = {name: 'andy'
}
// 1. call()可以调用函数
fn.call()
// 2. call()可以改变这个函数的this指向 此时这个函数的this就指向了o这个对象
fn.call(o)
利用父构造函数继承属性
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {// this指向父构造函数的对象实例this.uname = unamethis.age = age
}
// 2. 子构造函数
function Son(uname, age, score) {// this指向子构造函数的对象实例// 调用父构造函数 但是要把父构造函数中的this修改为子中的thisFather.call(this, uname, age)this.score = score
}var son = new Son('ldh', 22, 100)
console.log(son)
利用原型对象继承方法
// 借用父构造函数继承属性
// 1. 父构造函数
function Father(uname, age) {// this指向父构造函数的对象实例this.uname = unamethis.age = age
}Father.prototype.money = function() {console.log(10000)
}
// 2. 子构造函数
function Son(uname, age, score) {// this指向子构造函数的对象实例// 调用父构造函数 但是要把父构造函数中的this修改为子中的thisFather.call(this, uname, age)this.score = score
}
// Son.prototype = Father.prototype 直接赋值有问题,修改了子 父也变了
Son.prototype = new Father()
// 如果利用对象的形式修改了原型对象 要记得利用constructor指回原来的构造函数
Son.prototype.constructor = Son
Son.prototype.exam = function() {console.log('考试')
}var son = new Son('ldh', 22, 100)
console.log(son)
类的本质
es6之前通过 构造函数+ 原型实现面向对象编程
es6通过类实现面向对象编程
- class的本质还是function
类的本质还是一个函数,可以简单的认为 类就是构造函数的另外一种写法
构造函数有原型对象prototype
构造函数有原型对象prototype 里面的constructor指向构造函数本身
构造函数可以通过原型对象添加方法
构造函数创建的实例对象有__proto__原型 指向构造函数的原型对象
所以es6的类其实就是构造函数的语法糖
新增的方法
迭代(遍历数组)forEach
forEach() \ map() filter() some() every()
array.forEach(functin(currentValue, index, arr))
currentValue: 数组当前项的值
index:数组当前项的索引
arr:数组对象本身
var arr = [1,2,3]
var sum = 0
arr.forEach(function(currentValue, index, array) {console.log('每个数组元素' + currentValue)console.log('每个数组元素的索引' + index)console.log('数组' + array)sum += currentValue
})
filter
array.filter(function(currentValue, index, arr))
filter()方法创建一个新的数组 新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
注意:它直接返回一个新数组
currentValue:数组的当前项的值
index:数组当前项的缩影
arr:数组对象本身
var arr = [12, 66, 4, 88]
var newArr = arr.filter(function(currentValue, index) {return currentValue > 20
})
console.log(newArr)//[66, 88]
some
array.some(function(currentValue, index, arr))
some方法用于监测数组中的元素是否满足指定条件
就是查找数组中是否有满足条件的元素
注意:它返回值是布尔值,如果查找到这个元素就返回true,如果查找不到就返回false
如果找到第一个满足条件的元素,则终止循环,不再继续查找
currentValue:数组当前项的值
index:数组当前索引
arr: 数组对象本身
// some 查找数组中是否有满足条件的元素
var arr = [10, 30, 4]
var flag = arr.some(function(value) {return value>20
})
console.log(flag)//true
filter也是查找满足条件的元素 返回的是一个数组,而且是把所有满足条件的元素都返回回来
some也是查找满足条件的元素 返回的是一个布尔值,如果查找到第一个满足条件的元素就终止循环
map和forEach相似 every()和some()相似
查询商品案例
渲染页面数据
// 利用新增数组方法操作数据var data = [{id: 1,pname: '小米',price: 3999},{id: 2,pname: 'oppo',price: 999},{id: 3,pname: '荣耀',price: 1299},{id: 4,pname: '华为',price: 1999}]// 1.获取相应的元素var tbody = document.querySelector('tbody')// 2. 把数据渲染到页面中data.forEach(function(currentValue, index) {var tr = document.createElement('tr')tr.innerHTML = `<td>${currentValue.id}</td><td>${currentValue.pname}</td><td>${currentValue.price}</td>`tbody.appendChild(tr)})``
### 根据价格筛选商品```js
// 利用新增数组方法操作数据var data = [{id: 1,pname: '小米',price: 3999},{id: 2,pname: 'oppo',price: 999},{id: 3,pname: '荣耀',price: 1299},{id: 4,pname: '华为',price: 1999}]// 1.获取相应的元素var tbody = document.querySelector('tbody')var search_price = document.querySelector('.search-price')var start = document.querySelector('.start')var end = document.querySelector('.end')setData(data)// 2. 把数据渲染到页面中function setData(myData) {// 先清空原来的tbody里面的数据tbody.innerHTML = ''myData.forEach(function(currentValue, index) {var tr = document.createElement('tr')tr.innerHTML = `<td>${currentValue.id}</td><td>${currentValue.pname}</td><td>${currentValue.price}</td>`tbody.appendChild(tr)})}// 3. 根据价格查询商品// 当我们点击了按钮,就可以根据我们的商品价格去筛选数组里面的对象search_price.addEventListener('click', function() {var newData = data.filter(function(currentValue) {var min = start.value var max = end.valuereturn currentValue.price >= min && currentValue.price <= max})setData(newData)})
根据商品名称筛选商品
// 4.根据商品名称查找商品// 如果查询数组中唯一的元素,用some方法更适合,因为它找到这个元素,就不在进行循环,效率更高search_pro.addEventListener('click', function() {var arr = []data.some(function(value) {if(value.pname === product.value) {arr.push(value)return true}})// 把拿到的数据 渲染到页面当中setData(arr)})
代码
some和forEach的区别
forEach里面的return不会终止迭代
some里面遇到return true就是终止遍历,迭代效率更高
trim
trim()方法会从一个字符串的两端删除空白字符
str.trim()
不影响字符串本身 返回的是一个新的字符串
Object.defineProperty()定义新属性或者修改原来的属性
Object.defineProperty(obj, prop, descriptor)
obj:必须,目标对象
prop: 必需,需定义或修改的属性的名字
desciptor:必须,目标属性所拥有的特性
第三个参数decriptor说明:以对象的形式书写
value:设置属性的值,默认为undefined
writable:值是否可以重写, ture|false, 默认为false
enumerable: 目标属性是否可以被枚举, ture|false, 默认为false
configurable: 目标属性是否可以被删除或是否可以再次修改特性 ture|false, 默认为false
函数进阶
函数的定义和调用
命名函数function fn()
匿名函数var fun = function(){}
new Function('参数1', '参数2', '函数体')
所有函数都是Function的实例(对象)
函数的调用方式
// 1. 普通函数
function fn() {console.log('ccc')
}
fn()
fn.call()// 2. 对象方法
var o = {sayHi: function() {console.log('obj')}
}
o.sayHi()// 3. 构造函数
function Star() {}
new Star()
// 4.绑定事件函数
// btn.onclick = function() {}//点击按钮就调用
// 5.定时器函数
setInterval(function(){}, 1000)//自动1秒调用一次
// 6.立即执行函数
(function(){console.log('ccc')
})()
//立即执行函数是自动调用
函数内this的指向
this的执行,是当调用函数的时候确定的,调用的方式的不同,决定了this指向的不同
一般指向调用者
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法所属对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
改变函数内部this指向
bind() call() apply()
call()
// call()
var o = {name: 'andy'
}function fn(a, b) {console.log(this)console.log(a + b)
}fn.call(o, 1, 3)
// call可以调用函数 可以改变函数内this指向
function Father(uname, age, sex) {this.uname = unamethis.age = agethis.sex = sex
}
function Son(uname, age, sex) {Father.call(this, uname, age, sex)
}
var son = new Son('ldh', 39, '男')
console.log(son)
//{ name: 'andy' }
//4
//Son { uname: 'ldh', age: 39, sex: '男' }
apply
apply()方法调用一个函数,简单理解为调用函数的方式,但是它可以改变函数this指向
fun.apply(thisArg, [argsArray[)
this.Arg: 在fun函数运行时指定的this值
argsArray: 传递的值,必须包含咋数组里面
返回值就是函数的返回值,因为它就是调用函数
var o = {name: 'andy'
}
function fn(arr) {console.log(this)console.log(arr)
}fn.apply(o, ['blue'])
// apply的主要应用 比如说可以利用apply借助数学内置对象求最大值
var arr = [1, 66, 3, 99, 19]
var max = Math.max.apply(Math, arr)
var min = Math.min.apply(Math, arr)
console.log(max, min)
-----------------
运行结果:
{ name: 'andy' }
blue
99 1
bind
bind()方法不会调用函数,但是能改变函数内部this指向
fun.bind(thisArg, arg1,arg2,...)
thisArg: 在fun函数运行时候指定的this值
arg1,arg2:传递的其他参数
返回由指定的this值和初始化参数改造的原函数拷贝
var o = {name: 'andy'
}
function fn(a, b) {console.log(this)console.log(a + b)
}var f = fn.bind(o, 1, 3)
f()
// 1, 不会调用原来的函数 可以改变原来函数内部的this指向
// 2. 返回的是原函数改变this之后产生的新函数
运行结果:
{ name: 'andy' }
4
<body><button>按钮</button><script>// 3. 如果有的函数我们不要立即调用 但是又想要改变这个函数内部的this指向此时用bind// 案例: 一个按钮,点击之后,就禁用这个按钮,3秒钟之后开启这个按钮var btn = document.querySelector('button')btn.onclick = function() {this.disabled = true// var that = thissetTimeout( function() {// this.disabled = false // 定时器函数里面的this指向的是window// that.disabled = falsethis.disabled = false}.bind(this), 3000)//这个this指向的是btn}</script>
</body>
call apply bind总结
相同点:
都可以改变函数内部的this指向
区别点:
- call和apply会调用函数,并且改变函数内部this指向
- call和apply传递的参数不一样,call传递参数arg1, arg2形式,apply必须数组形式
- bind不会调用函数,返回一个新的函数
主要应用场景: - call经常做继承
- apply经常跟数组有关系,比如借助数学对象实现数组最大值最小值
- bind不调用函数,但是还想改变this指向时使用,比如改变定时器内部的this指向
严格模式
严格模式中的变化
变量名必须先声明再使用
不能随意删除已经声明好的变量
严格模式下this指向问题
- 以前在全局作用域中的this指向window对象
- 严格模式下全局作用域中函数中的this是undefined
- 以前构造函数时不加new也可以调用,当普通函数,this指向全局对象
- 严格模式下,如果构造函数不加new调用,this会报错
- new实例化的构造函数指向创建的对象实例
- 定时器this还是指向window
- 事件 对象还是调用者
函数变化
参考mdn
高阶函数
高阶函数时对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出
函数也是一种数据类型,同样可以作为参数,传递给 另一个参数使用,最典型的就是回调函数
function fn(a, b, callback) {console.log(a + b)callback && callback()
}
fn(1, 3, function(){console.log('我是最后调用的')
})
运行结果:
4
我是最后调用的
闭包
闭包指有权访问另一个函数作用域中变量的函数
简单理解就是,一个作用域可以访问另外一个函数内部的局部变量
闭包的主要作用:延伸了变量的作用范围
闭包应用
点击li打印当前索引号
<body><ul class="nav"><li>榴莲</li><li>臭豆腐</li><li>螺蛳粉</li><li>鲱鱼罐头</li></ul><script>var lis = document.querySelector('.nav').querySelectorAll('li')// 1. 利用动态添加属性的方式// for(var i = 0; i < lis.length; i++) {// // 异步任务 点击之后才触发// lis[i].index = i// lis[i].onclick = function() {// console.log(this.index)// }// }// 2. 利用闭包的方式for(var i = 0; i < lis.length; i ++) {// 利用for循环创建了4个立即执行函数(function(j){// console.log(j)lis[i].onclick = function() {console.log(j)}})(i)}</script>
</body>
3秒钟后打印
<body><ul class="nav"><li>榴莲</li><li>臭豆腐</li><li>螺蛳粉</li><li>鲱鱼罐头</li></ul><script>var lis = document.querySelector('.nav').querySelectorAll('li')for(var i = 0; i < lis.length; i++) {(function(i) {setTimeout(function(){console.log(lis[i].innerHTML)}, 3000)})(i)}</script>
</body>
计算打车价格
// 闭包应用-计算打车价格
// 打车起步价13(3公里内) 之后每多一公里增加5块钱 用户输入公里数就可以计算打车价格
// 如果有拥堵情况 总价格多收取10块钱拥堵费
var car = (function () {var start = 13; //起步价 局部变量var total = 0; //局部变量return {price: function (n) {if (n <= 3) {total = start} else {total = start + (n - 3) * 5}return total},yd: function (flag) {return flag ? total + 10 : total}}
})()console.log(car.price(5))
console.log(car.yd(true))
console.log(car.price(1))
思考题
// 思考题 1:var name = "The Window";var object = {name: "My Object",getNameFunc: function() {return function() {return this.name;};}};console.log(object.getNameFunc()())var f = object.getNameFunc();// 类似于var f = function() {return this.name;}f();// 思考题 2:// var name = "The Window"; // var object = { // name: "My Object",// getNameFunc: function() {// var that = this;// return function() {// return that.name;// };// }// };// console.log(object.getNameFunc()())
递归
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
利用递归求阶乘
function fn(n) {if(n === 0 || n === 1) return 1else return fn(n - 1) * n
}
console.log(fn(5)) // 120
利用递归求斐波那契
// 1 1 2 3 5 8 13 21
function fib(n) {if(n === 1 || n === 2) return 1else return fib(n - 1) + fib (n - 2)
}
console.log(fib(8))
利用递归遍历数据
var data = [{id: 1,name: '家电',goods: [{id: 11,gname: '冰箱',goods: [{id: 111,gname: '海尔'},{id: 222,gname: '格力'}]},{id: 12,gname: '洗衣机'}]},{id: 2,name: '服饰'}
]// 输入id号,就可以返回的数据对象
// 1. 利用forEach去遍历里面每一个对象
function getID(data, id) {var o = {}data.forEach(function(item) {// console.log(item) // 2个对象if(item.id === id) {// console.log(item)o = item// 想要得到里层的数据 11 12要利用递归函数// 里面要有goods数组 并且数组长度不为0}else if(item.goods && item.goods.length > 0) {o = getID(item.goods, id)}})return o
}
console.log(getID(data, 1))
console.log(getID(data, 11))
console.log(getID(data, 111))
浅拷贝和深拷贝
浅拷贝只是拷贝一层,更深层次对象级别只拷贝了引用
深拷贝拷贝多层,每一级别的数据都会拷贝
Object.assign(target,…sources) es6新增方法可以浅拷贝
var obj = {id: 1,name: 'andy',msg: {age: 18,}
}var o = {}
for (var k in obj) {o[k] = obj[k]
}
console.log(o)
o.msg.age = 20
// 修改后 也影响了obj里面的数据 浅拷贝更深层次对象只拷贝了地址
console.log(o)
console.log(obj)
var obj = {id: 1,name: 'andy',msg: {age: 18,}
}var o = {}
Object.assign(o, obj)
console.log(o)
深拷贝
var obj = {id: 1,name: 'andy',msg: {age: 18},color: ['pink', 'red']
}var o = {}
// 封装函数
function deepCopy(newobj,oldobj) {for(var k in oldobj) {// 判断我们的属性值属于哪种类型
// 1.获取值 oldobj[k]var item = oldobj[k]
// 2. 判断这个值是否是数组if(item instanceof Array) {newobj[k] = []deepCopy(newobj[k], item)}else if(item instanceof Object) {// 3. 判断这个值是否是对象newobj[k] = {}deepCopy(newobj[k], item)}else {// 4. 属于简单数据类型newobj[k] = item}}
}deepCopy(o, obj)
console.log(o)
正则表达式
正则表达式:是用于匹配字符串中字符组合的模式。
在javascript中,正则表达式也是对象
- 验证表单
- 过滤页面中的一些敏感词
- 获取字符串中的特定部分
实际开发中,一般都是直接复制写好的正则表达式
正则表达式在javascript中的使用
两种方式创建正则表单式
var regexp = new RegExp(/123/)
console.log(regexp)// 2. 利用字面量创建
var rg = /123/
测试正则表达式 test() 返回true false
regexObj.text(str)
regexObj是写的是正则表达式
str是我们要测试的文本
var rg = /123/
console.log(rg.test(123))//true
console.log(rg.test('abc'))//false
正则表达式里面的特殊字符
可以参考MDN官网
边界符
// 边界符 ^ $
// ^开始 $结尾
// 正则表达式不需要加引号 无论是数字还是字符串
var rg = /abc/
console.log(rg.test('abc')) // true
console.log(rg.test('aabc')) // true
var reg = /^abc/console.log(reg.test('aabc')) // false
var regx = /^abc$/
console.log(regx.test('abcd')) // false
字符类
表示有一系列字符可供选择,只要匹配其中一个就可以了[]
var rg = /[abc]/ // 只要包含有a 或者有b 或者c 都返回true
console.log(rg.test('andy')) //true
console.log(rg.test('babay'))// truevar rgx = /^[abc]$/ // 三选一 只有是a 或者b 或者c
console.log(rgx.test('a')) // true
console.log(rgx.test('ab')) // falsevar rg2 = /^[a-z]$/ // 26个英文字母任何一个字母 都返回true
console.log(rg2.test('a')) // true
// 字符组合
var reg = /^[a-zA-Z0-9_-]$/ // 26个英文字母和任何数字 都返回true
console.log(reg.test('9')) // true
console.log(reg.test('_')) //true// 如果中括号里面有^ 表示取反的意思 千万不要和边界符^混淆
var reg2 = /^[^a-zA-Z0-9_-]$/
console.log(reg2.test('a')) // false
量词符
量词符用来设定某个模式出现的次数
量词 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或者是更多次 |
{n, m} | 重复n到m次 |
var reg = /^a*$/
console.log(reg.test('')) // true
console.log(reg.test('aaa')) // truevar reg2 = /^a+$/
console.log(reg2.test('')) // false
console.log(reg2.test('aaaa')) //truevar reg3 = /^a{3}$/
console.log(reg3.test('aaa')) // true
量词重复某个模式的次数
// 量词是设定某个模式出现的次数
var reg = /^[a-zA-Z0-9_-]{6,16}$/console.log(reg.test('baby13')) // true
console.log(reg.test('baby')) // false
用户名验证
功能需求:
- 如果用户名输入合法,则后面提示信息为:用户名合法并且颜色为绿色
- 如果用户名不合法,则后面提示信息为:用户名不符合规范 并且颜色红色
分析;
3. 用户名只能为英文字母,数字,下划线或者短横线组成,并且用户名长度为6-16位
4. 首先准备好这种正则表达式模式
5. 当表单失去焦点时开始验证
6. 如果符合正则规范,则让后面的span标签添加right类
7. 如果不符合正则规范,则让后面的span标签添加wrong类
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>JS Bin</title>
</head>
<body>
<input type="text" class="uname"><span>请输入用户名</span>
</body>
</html>
.right{color: green;
}.wrong {color: red;
}
let uname = document.querySelector('.uname')
let span = document.querySelector('span')
let reg = /^[a-zA-Z0-9_-]{6,16}$/
uname.onblur = function() {if(reg.test(this.value)) {span.classList.add('right')span.innerHTML = '用户名格式输入正确'}else {span.classList.add('wrong')span.innerHTML = '用户名格式输入错误'}
}
括号总结
// 中括号 字符集合 匹配方括号中的任意字符
var reg = /^[abc]$/// 大括号 量词符 里面表示重复次数
var reg = /^abc{3}$/ // 它只是让c重复三次 abccc// 小括号 表示优先级
var reg = /^(abc){3}$/ // 它是让abc 重复三次 abcabcabc
在线测试
预定义类
预定义类指的是某些常见模式的简写方式
预定类 | 说明 |
---|---|
\d | 匹配0-9之间的任意数字,相当于[0-9] |
\D | 匹配所有0-9意外的数字,相当于[^0-9] |
\w | 匹配任意的字母、数字和下划线 相当于[a-zA-Z0-9] |
\W | 除了所有字母、数字和下划线以外的字符 相当于[^a-zA-Z0-9] |
\s | 匹配空格(包括换行符 制表符 空格符等) 相当于[\t\r\n\v\f] |
\S | 匹配非空格的字符 相当于[\t\r\n\v\f] |
座机号码验证
// 座机号码验证: 全国座机号码
// 两种格式: 010-12345678 或者 0530-1234567var reg = /^\d{3,4}-\d{7,8}$/
表单验证
正则替换
replace()方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式
stringObject.replace(regexp/substr, replacement)
- 第一个参数: 被替换的字符串或正则表达式
- 第二个参数: 替换为的字符串
返回值是一个替换完毕的新的字符串
var str = 'andyAndbaby'
var newStr = str.replace('andy', 'baby')
console.log(newStr) //"babyAndbaby"
正则表达式参数
/表达式/[switch]
switch也称为修饰符 按照什么样的模式来匹配 有三种值:
g: 全局匹配
i: 忽略大小写
gi: 全局匹配+忽略大小写
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>JS Bin</title>
</head>
<body><textarea name="message" id="message" cols="30" rows="10" ></textarea><button>提交</button><div></div>
</body>
</html>
var text = document.querySelector('#message')
var btn = document.querySelector('button')
var div = document.querySelector('div')
btn.onclick = function() {div.innerHTML = text.value.replace(/激情|gay/g, '**')
}
javascript进阶面向对象ES6相关推荐
- JavaScript进阶面向对象ES6整合篇
整合篇整体内容比较多,详细点可以查询目录 文章目录 一.JavaScript面向对象 1.面向对象编程介绍 2.ES6中的类和对象 3.类的继承 4.案例:面向对象案例 二.构造函数和原型 1. 构造 ...
- JavaScript 进阶面向对象ES6
ES6类的本质 ES6之前 → 通过 构造函数 + 原型 实现面向对象编程 构造函数有原型对象 prototype prototype里有 constructor 指向构造函数本身 可以通过原型对象添 ...
- JavaScript进阶(四)
JavaScript进阶(四) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...
- JavaScript进阶(二)
JavaScript进阶(二) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...
- JavaScript进阶(三)
JavaScript进阶(三) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...
- JavaScript进阶(一)
JavaScript进阶(一) 2019版黑马程序员javaScript进阶面向对象ES6 122集教程,哔哩哔哩链接:https://www.bilibili.com/video/BV1Kt411w ...
- 前端学习笔记——JavaScript进阶
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.JavaScript 面向对象 1. 面向对象编程介绍 1.1 两大编程思想 1.2 面向过程编程 1.3 面向对 ...
- JavaScript进阶-高级特性及ES6
任务 对象的扩展 let和const Object.keys() for...of 扩展运算符 Set 和 Map 模版字符串 默认参数 rest参数 箭头函数 解构赋值 对象的扩展 方法的简写 // ...
- JavaScript 进阶教程(1)--面向对象编程
目录 1 学习目标 2 面向对象介绍 2.1 什么是对象 2.2 什么是面向对象 2.3 JavaScript 中面向对象的基本体现 3 JavaScript 如何创建对象 3.1 字面量方式 3.2 ...
- JavaScript进阶1-学习笔记
文章目录 JavaScript进阶1 预解析 作用域 面向对象的写法 JavaScript进阶1 预解析 //预解析 //1) console.log(a); var a = 1; //解析过程 va ...
最新文章
- 山寨c 标准库中的getline 函数
- STM32 进阶教程 15 - 串口DMA收发
- ubuntu装jdk
- 分布式文件系统研究-搭建图片服务虚拟主机
- mega2560单片机开发_[MEGA DEAL] Ultimate Java开发和认证指南(59%折扣)
- tcp校验和计算校验和例子_OSI参考模型和TCP/IP参考模型
- python图片顶端_用python进行图片整理
- caffe数据集——LMDB
- 使用补丁修改DSDT/SSDT [DSDT/SSDT综合教程]
- 安装软件提示计算机管理员权限,win10安装软件需要管理员权限的处理办法|win10装软件提示error launching installer如何处理?...
- stm32万年历流程图_基于 STM32 RTC的万年历
- windows xp sp2的产品密钥
- 360手机如何修改服务器,360路由器手机怎么设置_手机如何设置360路由器? - 192路由网...
- autocad网络服务器如何安装许可证,使用网络许可选项文件的步骤
- Javascript错误处理——try...catch
- J storm战队成员_DOTA2J.Storm战队介绍-DOTA2梦幻联赛S11预选赛J.Storm战队介绍_牛游戏网攻略...
- wireshark 分析理解DHCP流程
- leaflet的引入
- 单点登陆(SSO)协议简介:OpenID、OAuth2、SAML
- html class函数,wordpress函数sanitize_html_class()用法示例