两大编程思想
面向过程POP
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步步实现,使用的时候再一个一个的依次调用就可以了。
即按照分析好的步骤解决问题
面向对象OOP
就是把事务分解成一个个对象,然后由对象之间分工与合作
即先找出对象,然后写出对象的功能,最后让对象使用功能
特性
- 封装性
- 继承性
- 多态性
思维特点
- 抽取(抽象)对象公用的属性和行为组织(封装)成一个类(模板)
- 对类进行实例化,获取类的对象
类class
ES6中新增了类的概念,可以用关键词class声明一个类,之后可以用这个类来实例化对象
- 类抽象了对象的公共部分,它泛指某一大类
- 对象特指某一个,通过实例化一个具体的对象
创建类
class classname { ……………… }
创建实例
var obj = new name();
constructor构造函数
constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个constructor()
class Star{ constructor(name){ this.uname = name; } } var ldh = new Star('刘德华'); console.log(ldh.uname); 输出结果为 刘德华
注意
- 通过class关键字创建类,类名我们还是习惯性定义首字母大写
- 类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
- constructor函数,只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
- 生成实例时,new不能省略
- 最后注意语法规范,创建类时,类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加function
在类中添加方法
直接写到类里即可
class Person { constructor(name,age){ this.name = name; this.age = age; } say(){ console.log(this.name+'你好'); } }
注意
- 类里面所有的函数不需要写function
- 多个函数方法之间不需要添加逗号分隔
类的继承
继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的。如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
extends关键字
class Father{ …… } class Son extends Father{ …… }
super关键字
用于访问和调用对象父类上的函数,可以调用父类的构造函数和普通函数
调用父类的构造函数 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(x,y); /调用了父类中的构造函数 } } var son = new Son(1,2); son.sum();
调用父类的普通函数 class Father{ say(){ return'我是爸爸'; } } class son extends Father{ say(){ console.log(super.say()+'的儿子'); } } var son = new son(); son.say();
子类继承父类方法同时扩展自己的方法
super必须写在子类构造函数this之前
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必须写在子类构造函数this之前 super(x,y); this.x = x; this.y = y; } subtract(){ console.log(this.x this.y); } var son = new Son(5,3); son.subtract(); son.sum();
类和对象的三个注意点
在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
类里面的共有的属性和方法一定要加this使用
类里面的this指向问题
- constructor里面的this指向的是创建的实例对象
- 谁调用了函数,这个函数里的this就指向谁(即指向方法的调用者)
- 如果想调用别的方法里的this,先在外面声明一个全局变量(如that)把那个方法里的this赋值给变量that,即“that = this”,然后再调用that即可达到目的
类的本质
- class的本质还是函数,我们也可以把class看做构造函数的另一种写法
- 类的所有方法都定义在类的prototype属性上
- 类创建的实例,里面也有__proto__指向类的prototype原型对象
- 所以ES6的类的绝大部分功能,ES5都可以做到,新的clss写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
- 所以ES6的类其实就是语法糖
- 语法糖:语法糖就是一种便捷写法,简单理解就是有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖
构造函数和原型
创建对象的方法
- 对象字面量
- new object()
- 自定义构造函数
- 首字母大写
- 调用时用new
new在执行时会做的四件事情
- 让内存中创建一个新的空对象
- 让this指向这个新的对象
- 执行构造函数里面的代码,给这个新对象添加属性和方法
- 返回这个新对象(不需要return)
实例成员和静态成员
实例成员:构造函数内部通过this添加的成员,只能通过实例化对象来访问(如uname,age)
静态成员:直接在构造函数本身上添加的成员,只能通过构造函数访问
function Star(uname,age){ this.uname = uname; this.age = age; this.sing = function(){ console.1og('我会唱歌'); } } var ldh = new Star('刘德华',18); 实例成员就是构造函数内部通过this添加的成员uname age sing就是实例成员,只能通过实例化的对象来访问 Star.sex='男'; 静态成员在构造函数本身上添加的成员 sex console.log(Star.sex); 静态成员只能通过构造函数来访问
原型对象prototype
- 为了解决构造函数实例化时存在的浪费内存的问题
- 构造函数通过原型分配的函数是所有对象共享的
- 可以把不变的方法,直接定义在prototype上,这样所有对象的实例就可以共享这些方法
- 使用方法
function fn (){ xxx; } fun.prototype.test = function(){ console.log('test()'); }
对象原型__proto__
对象都会有一个属性__proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。
方法的查找规则
首先先看ldh对象身上是否有sing方法,如果有就执行这个对象上的sing,如果没有sing这个方法,因为有__proto__的存在,就去构造函数原型对象prototype.身上去查找sing这个方法
constructor构造函数
- 对象原型(__proto__)和构造函数(prototype)原型对像里面都有一个属性constructor属性,constructor称为构造函数,因为它指回构造函数本身。
- 主要用于记录该对象引用于哪个构造函数,他可以让原型对象重新指向原来的构造函数
- 如果我们修改了原来的原型对象,给原型对象赋值的一个对象,则必须手动的利用constructor指回原来的构造函数
Star.prototype = { constructor:Star; }
原型链
对象成员查找规则
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性
- 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)
- 如果还没有就查找原型对象的原型(Object的原型对象)
- 依此类推一直找到Object为止(null)
原型对象this指示
在构造函数中,里面的this指向的是对象实例,谁调用指向谁
原型对象prototype里面的this指向的是实例对象,谁调用指向谁
扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法,比如给数组增加自定义求和的功能
console.log(Array.prototype); Array.prototype.sum function(){ var sum =0; for (var i=0;i<this.length;i++){ sum +this[i]; } return sum; } var arr=[1,2,3]; console.log(arr.sum());
注意:数组和字符串内置对象里,不能给原型对象这样赋值Array.prototype={},只能Array.prototype.xxx = function(){},不然会覆盖原来的
继承
在ES6之前,可以通过构造函数+原型对象模拟实现继承,称为组合继承
call
作用:调用这个函数,并且可以修改this的指向
fn.call(thisArg,arg1,arg2,……)
- thisArg:当前调用函数this指向的对象
- arg1,arg2:传递的普通参数
借用构造函数继承父类属性
核心原理:通过call()把父类型的this指向子类型的this,这样就可以实现继承效果
做法:在子构造函数中添加类似“Father.call(this,xxx,xxx)”即可实现
function Father(uname,age){ this.uname = uname; this.age = age; } 子构造函数 function Son(uname,age){ Father.call(this,uname,age); } var son=new Son('刘德华',18); console.log(son);
借用原型对象继承父类方法
不能直接把父赋值子:子元素修改时,也会带动父元素修改
可以用new实例化父类
数组方法
迭代遍历方法 forEach()
array.forEach(function (value,index,arr){}) 。value:数组当前项的值 。index:数组当前项的索引 。arr:数组对象本身 实例:遍历数组 var arr=[1,2,3]; arr.forEadh(function(value,index,array){ console.1og('每个数组元素'+value); console.log('每个数组元素的索引号'+index); console.log('数组本身'+array); })
迭代筛选方法 filter()
- filter()方法是创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要于筛选数组
- 注意它直接返回一个新数组
array.filter (function(value,index,arr){}) value:数组当前项的值 index:数组当前项的索引 arr:数组对象本身 实例:筛选大于数组中20的数 var arr=[12,66,4,88]; var newArr = arr.filter(function(value,index){ return value >= 20; }); console.log(newArr);
迭代判断方法 some()
- some()方法用于检测数组中的元素是否满足指定条件,通俗点就是,查找数组中是否有满足条件的元素
- 注意它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false.
- 如果找到第一个满足条件的元素,则终止循环不再继续查找.
array.some(function(value,index,arr){}) value:数组当前项的值 index:数组当前项的索引 arr:数组对象本身 实例:判断数组中是否有大于20的数 var arr [10x 30,4]; var flag = arr.some(function(value){ return value >=20; }); console.log(flag);
字符串方法
trim()方法
把一个字符串两端的空白字符删除,并返回一个新的字符串
str.trim();
并不影响字符串本身,他返回的是一个新的字符串
对象方法
Object.defineProperty()
作用:定义新属性或修改原有的属性
object.defineProperty(obj,prop,descriptor) obj:目标对象 prop:需添加或修改的属性的名字 descriptor:需要添加或修改的属性,需要用对象形式书写{xxx} 这三个值都是必须填的 enumerable:目标属性是否可以被枚举 true false默认为false configurable:目标属性是否可以被删除或是否可以再次修改,特性true|false默认为false 实例: var obj ={ id:1, pname:'小米', price:1999 }; value:设置属性的值,默认为undefined object.defineProperty(obj,'num',{ value:1000 }); console.log(obj); writable:值是否可以重写,默认为false Object.defineProperty(obj,'id',{ writable:false, }); console.log(obj); enumerable:目标属性是否可以被枚举,默认为false object.defineProperty(obj,'address',{ value:'中国山东蓝翔技校xx单元', writable:false, enumerable:false }); console.log(object.keys(obj)); configurable:目标属性是否可以被删除或是否可以再次修改特性,默认为false object.defineProperty(obj,'address',{ value:·中国山东蓝翔技校xx单元', writable:false, enumerable:false, configurable:false }); delete obj.address; console.log(obj);
Object.keys()
- 用于获取对象自身所有的属性名
- 效果类似for…in
- 返回一个由属性名组成的数组
object.keys (obj)
函数进阶
利用构造函数定义函数
Function里需要是'xxx'字符串格式
语法:new Function('参数1','参数2','函数体') var f = new Function('a','b','console.log(a + b)'; f(1,2);
函数的调用方式
- 普通函数:fn() 或 fn.call()
- 对象的方法:object.fn()
- 构造函数:new Fn()
- 绑定事件函数:触发绑定事件
- 定时器函数:时间到了就触发
- 立即执行函数: 自动调用
函数内部的指向问题
- 普通函数:window
- 构造函数:实例对象
- 对象方法:该方法所属对象
- 事件绑定:绑定事件对象
- 定时器函数:window
- 立即执行函数:window
改变函数内部this指向
call()
fn.call(thisrg,arg1,arg2,…) var o ={ name:'andy' } function fn(){ console.log(this); }; fn.call(o);
bind()
- 不会调用函数,但是可以改变函数内部this指向
- 返回的是原函数改变this之后产生的新函数
- 应用:不想立即执行函数,但是想改变这个函数this的指向
fn.bind(thisArg,arg1,arg2,……); /*thisArg:在fun函数运行时指定的this值 arg1,arg2:传递的其他参数 返回由指定的ths值和初始化参数改造的原函数拷贝*/
apply()
- 也是调用函数,thisArg可以改变函数的指向
- 但是参数必须是数组(伪数组)
- 主要应用:对数组进项操作
fn.apply(thisArg,[argsArray]); /*thisArg:在fn函数运行指定的this值 argsArray:传递的值,必须包含在数组里面 返回值就是函数的返回值,因为它就是调用函数*/ var o ={ name:andy' }; function fn(arr) { console.log(this); console.log(arr); }; fn.apply(o,['pink']);
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或函数作为返回值(闭包)输出
函数作为参数
function fn(a,b,callback){ console.log(a b); callback &callback(); } fn(1,2,function(){ console.1og('我是最后调用的'); }
函数闭包
是什么
- 闭包指有权访问另一个函数作用于中变量的函数
- 简单理解就是,一个作用域可以访问另外一个函数内部的局部变量
作用
延伸了变量的作用范围
函数递归
- 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
- 简单理解:函数内部自己调用自己,这个函数就是递归函数
- 递归函数的作用和循环效果一样
- 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
var num 1; function fn(){ console.1og('我要打印6句话'); if (num =6){ return;/递归里面必须加退出条件 } num++; fn(); } fn();
严格模式 strict mode
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为,“为脚本开启严格模式”和“为函数开启严格模式”
为脚本开启严格模式
如何开启:在所有语句之前放一个特定语句“use strict”;(或'use strict';)
为函数开启严格模式
如何开启:在函数所有语句之前放一个特定语句“use strict”;(或'use strict';)
严格模式下的变化
变量规定
- 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用。
- 严禁删除已经声明变量。例如,delete x; 语法是错误的
严格模式下this指向问题
- 以前在全局作用域函数中的this指向window对象,严格模式下全局作用域中函数中的this是undefined。
- 以前构造函数时不加new也可以调用,当作普通函数,this指向全局对象。严格模式下,如果构造函数不加new调用,this会报错。
- new实例化的构造函数指向创建的对像实例
- 定时器this还是指向window。
- 事件、对象还是指向调用者。
严格模式下函数变化
- 函数不能有重名的参数
- 函数必须声明在顶层,为了与新版本接轨,不允许在非函数的代码块内声明函数。
正则表达式
介绍
- 正则表达式是用于匹配字符串中字符组合的模式
- 正则表达式也是对象
- 作用
- 正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验正表单:用户名表单只能输入英文字母、数字或者下划线,呢称输入框中可以输入中文(匹配)。
- 正则表达式还常用于过滤掉页面内容中的些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
创建正则表达式
通过调用RegExp对象的构造函数创建
var 变量名 = new RegExp(/表达式/);
利用字面量创建
var 变量名 = /表达式/;
测试正则表达式 test
test()正则对象方法,用于检测字符串是否符合该规则,符合返回true,其参数是测试字符串
regexobj.test(str); regexobj是写的正则表达式 str是我们要测试的文本 就是检测str文本是否符合我们写的正则表达式规范
正则表达式的组成
一个正则表达式可以由简单的字符构成,比如/bc/,也可以是简单和特殊字符的组合,比如/ab*c/。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^ 、$、+等。
正则表达式不需要加引号,不管数组型还是字符串型
/abc/里,只要包含abc这个字符串返回的都是true
边界符
- ^:表示匹配文本以谁开始
- $:表示匹配文本以谁结束
- 如果是^abc$,则指必须是abc这三个字符才行
字符类[]
- 表示有一系列字符可供选择,只要匹配其中一个就可以了
- 只要想包含,直接写到[xxx]里即可,不用考虑顺序
- 可以写入符号,如:_ -等
- 如果在[xxx]里开头写上^,则为取反,即不能取到[xxx]内的文本
var rg = /[abc]/; 只要包含有a或者包含有b或者包含有c都返回为true var rg = /^[abc]$/; 三选一只有是a或者是b或者是c这三个字母才返回true var reg = /^[a-z]$/; 26个英文字母任何一个小写字母返回true var reg = /^[a-zA-Z]$/; 26个英文字母任何大小写字母返回true,表示的是a到z的范围 var reg1 = /^[^a-zA-Z0-9_-]$/; 取反,除了[]里的都返回ture
量词符
用来设定某个模式出现的次数
- *: 重复0次及以上
- +: 重复1次及以上
- ?: 重复0次或1次
- {n}: 重复n次
- {n,}: 重复n次及以上
- {n,m}: 重复n到m次
* 相当于 >= 0 可以出现0次或者很多次 var reg = /a*$/; console.log(reg.test('')); console.log(reg.test('a')); console.log(reg.test('aaaa')); +相当于>=1可以出现1次或者很多次 var reg = /^a+$/; console.log(reg.test('')); false console.log(reg.test('a')); true console.log(reg.test('aaaa')); true ? 相当于1次或者0次 var reg = /a?$/; console.log(reg.test('')); true console.log(reg.test('a')); true console.log(reg.test('aaaa')); false {3} 就是重复3次 var reg = /a{3)$/; console.log(reg.test('')); false console.log(reg.test('a')); false console.log(reg.test('aaaa')); false {3,} 大于等于3 var reg = /a{3,}$/; console.log(reg.test('')); false console.log(reg.test('a')); false console.log(reg.test('aaaa')); true console.log(reg.test('aaa')); true {3,6} 大于等于3并且小于等于6 var reg = /^a{3,6}$/; console.log(reg.test('')); false console.log(reg.test('a')); false console.log(reg.test('aaaa')); true console.log(reg.test('aaa')); true console.log(reg.test('aaaaaaa')); false
量词重复某个模式的次数
var reg = /^[a-zA-Z0-9_-]{6,16}$/; 需要同时满足 [] 和 6-16个字符 这两个要求
预定义类
指的是常见模式的简写方式
- \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]
正则表达式参数
/表达式/switch
switch(也称为修饰符)按照什么样的模式来匹配,有三种值
- g:全局匹配
- i:忽略大小写
- gi:全局匹配+忽略大小写
div.innerHTML = text.value.replace(/激情/g,'**') 把文本中的激情换成**
正则替换
replace替换
- 可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式
- 返回一个新字符串
- 可以和正则表达式参数一起使用
stringObject.replace(regexp/substr,replacement) regexp/substr 被替换字符串 或 正则表达式 replacement 目标字符串