面向对象
面向过程:分析出解决问题所需的步骤,然后用函数把这些步骤一步步实现,使用的时候再依次调用就可以了。
- 优点︰性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
- 缺点︰没有面向对象易维护、易复用、易扩展
面向对象:将事务分解成一个个对象,然后由对象之间分工合作。
特性:封装性、继承性、多态性
- 优点∶易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
-
类和对象
对象
对象:对象是一个具体的事物
在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的: 属性:事物的特征,在对象中用属性来表示(常用名词)
-
类
在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象
类抽象了对象的公共部分,它泛指某一大类( class )
- 对象特指某一个,通过类实例化一个具体的对象
面向对象的思维特点:
1、抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
2、对类进行实例化,获取类的对象
创建类
constructor构造函数
(1)通过class关键字创建类,类名我们还是习惯性定义首字母大写
(2)类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
(3) constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
(4)生成实例new不能省略
(5)最后注意语法规范,创建类类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加function
类添加方法
(1)我们类里面所有的函数不需要写function
(2)多个函数方法之间不需要添加逗号分隔
extend 继承
现实中的继承∶子承父业,比如我们都继承了父亲的姓。
程序中的继承∶子类可以继承父类的一些属性和方法。
super 关键字
用于访问和调用对象父类上的函数。
可以调用父类的构造函数,也可以调用父类的普通函数。
- 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
- 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法
注意
- ES6中没有变量提升,因此应该先定义类,然后通过类实例化对象
- 类里面共有的属性和方法一定要加this使用

- 不用加小括号,如果加了小括号就是立马调用了
- 类里面的this的指向问题
- constructor里面的this指向的是创建的实例对象
- 函数里的this指向的是调用了这个方法的调用者

构造函数和原型
在典型的OOP的语言中(如Java ),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没用引入类的概念。
ES6,全称ECMAScript 6.0,2015.06发版。但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。
在ES6之前,对象不是基于类创建的,而是用一种称为构建函数的特殊函数来定义对象和它们的特征。
创建对象可以通过以下三种方式:
1.对象字面量
2.new Object()
3.自定义构造函数构造函数
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。
我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
在JS中,使用构造函数时要注意以下两点︰
1.构造函数用于创建某一类对象,其首字母要大写
2.构造函数要和new一起使用才有意义 :::warning new在执行时会做四件事情:
- constructor里面的this指向的是创建的实例对象
- 在内存中创建一个新的空对象。
- 让this指向这个新的对象。
- 执行构造函数里面的代码,给这个新对象添加属性和方法。
- 返回这个新对象(所以构造函数里面不需要return ) 。
:::
实例成员 静态成员
JavaScript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。
通过这两种方式添加的成员,就分别称为静态成员和实例成员。
- 静态成员︰在构造函数本身上添加的成员称为静态成员,只能由构造函数本身来访问
- 实例成员︰在构造函数内部创建的对象成员称为实例成员,只能由实例化的对象来访问
缺点
存在内存浪费的问题

输出false,因为在比较的时候比较的是地址
构造函数原型prototype
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。
注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
1.原型是什么?
一个对象,我们也称为prototype为原型对象。
⒉原型的作用是什么?
共享方法。
对象原型proto
对象都会有一个属性proto指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有proto原型的存在。
方法查找规则:先看实例对象上是否有该方法,如果有就执行该对象上的对应方法;如果没有这个方法,因为有proto的存在,就去构造函数原型对象prototype上去查找
constructor构造函数
对象原型(proto)和构造函数( prototype ) 原型对象里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
构造函数 & 实例 & 原型对象
原型链
对象成员查找机制
- 当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
- 如果没有就查找它的原型(也就是proto指向的prototype原型对象)。
- 如果还没有就查找原型对象的原型( Object的原型对象)。
- 依此类推一直找到Object为止 ( null )。
proto对象原型的意义就在于为对象成员查找机制提供一个方向,或者说─条路线。
原型对象this指向
constructor里面的this指向的是创建的实例对象
函数里面指向的是方法的调用者
扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。
比如:给数组增加自定义求偶数和的功能。
注意∶数组和字符串内置对象不能给原型对象覆盖操作Array.prototype = {},只能是Array.prototype.xxx = function(){}的方式.
console.log(Array.prototype);Array.prototype.sum = function() {var sum = 0;for (let i = 0; i < this.length; i++) {sum += this[i];}return sum;}let arr= [1,2,3];console.log(arr.sum());
继承
ES6之前并没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承.
call()
调用这个函数,并且修改函数运行时的this的指向fun.call(thisArg,arg1,arg2,...)
作用:
<a name="lk8F7"></a>## 借用原型对象的继承方法<br />此时不会调用父函数中的money方法<br />加上`Son.prototype = Father.prototype;`<br /><br />此时修改子对象中的原型对象也会修改父的原型对象<br />应该改为,此时,Father的实例对象瑜Father的原型对象的地址并不相同,因此再Son中修改不会影响到Father的原型对象<br />`Son.prototype = new Father();`<br />`Son.prototype.constructor = Son; `<br /><br /><a name="f3FzG"></a>## 类类的本质是一个函数<br />1.class本质还是function.<br />⒉.类的所有方法都定义在类的`prototype`属性上<br />3.类创建的实例,里面也有`_proto_`指向类的`prototype`原型对象<br />4.所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。<br />因此ES6的类其实是一个语法糖:::warning语法糖就是一种便捷写法、简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖:::<a name="n9O46"></a># ES5新增<a name="fAFUh"></a>## 字符串数组<a name="sYjKV"></a>### `forEach()````javascript// forEach迭代(遍历)数组var arr =[1,2,3];arr.forEach(function(value,inaex,array) {console.log('每个数组元素’+value);console.log( '每个数组元素的索引号’+index);console.log(数组本身’+array);});
filter()
- filter()方法:创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
- 注意它直接返回一个新数组
- currentValue:数组当前项的值
- index:数组当前项的索引
arr:数组对象本身
let arr = [12,66,4,88];let newArr = arr.filter(function(value, index) {return value >= 20;}
some()some()方法用于检测数组中的元素是否满足指定条件
- 通俗点查找数组中是否有满足条件的元素注意它返回值是布尔值,如果查找到这个元素,就返回true,如果查找不到就返回false.
- 如果找到第一个满足条件的元素,则终止循环不在继续查找.
currentValue:数组当前项的值index:数组当前项的索引arr:数组对象本身var arr = [10,30,4];var flag = arr.some(function(value) {return value >= 20;});
trim()对象方法
Object.defineProperty()定义对象中新属性或修改原有属性
object.defineProperty(obj, prop,descriptor)obj:必需。目标对象
- prop:必需。需定义或修改的属性的名字
- descriptor :必需。目标属性所拥有的特性

Object.defineProperty():第三个参数descriptor说明:以对象形式{}书写
value:设置属性的值默认为undefined
writable:值是否可以重写。true | false 默认为false
enumerable:目标属性是否可以被枚举。true | false默认为false
configurable:目标属性是否可以被删除或是否可以再次修改特性true | false 默认为false
Object.keys(obj)
用于获取对象自身所有的属性object.keys(obj)
- 效果类似for…in
-
函数进阶
函数的定义
// 1.自定义函数(命名函数)function fn() {};// 2.函数表达式(匿名函数)var fun = function() {};// 3.利用new Function( '参数1','参数2','函数体');var f = new function();
Function里面参数都必须是字符串格式
- 第三种方式执行效率低,也不方便书写,因此较少使用
- 所有函数都是Function的实例(对象)
- 函数也属于对象
函数的调用方式
1.普通函数
2.对象的方法
3.构造函数
4.绑定事件函数
5.定时器函数
6.立即执行函数
this
- 普通函数里this指向windows
- 对象的方法,this指向对象
- 构造函数中,this指向实例对象
- 绑定事件函数的this指向函数的调用者
- 计时器中,指向Windows
- 立即执行函数,指向Windows
改变函数内部this指向
bind
fun.bind(thisArg,arg1, arg2, ...)
- thisArg :在fun函数运行时指定的this值
- arg1 , arg2:传递的其他参数
不会调用原来的函数,可以改变函数内部的this指向,返回由指定的this值和初始化参数改造的原函数拷贝
如果函数不需要立即调用,又想改变函数的this指向,就用bind方法
// 我们有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮var btn = document.querySelector( ' button ' );var btn = document.queryselector('button');btn.onclick = function() {this.disabled = true; //这个this指向的是 btn 这个按钮var that = this;setTimeout(function() {that.disabled = false;//定时器函数里面的this指向的是window},308e)}}var btn = document.queryselector('button');btn.onclick = function(){this.disabled = true;//这个this指向的是 btn 这个按钮setTimeout(function() {windowthis.disabled = false;//定时器函数里面的this 指向的是window,现在利用bind改成调用者}.bind(this), 3000);var btns = document.querySelectorAl1('button');for (var i = 0; i < btns.length; i++) {btns[i].onclick = function() {this.disabled = true;setTimeout(function() {this.disabled = false;、}.bind(this),2000);}}
call
fn.call(Object,arg1,arg2,...)
// call第一个可以调用函数第二个可以改变函数内的this指向// call的主要作用可以实现继承function Father(uname,age, sex){this.uname = uname;this.age = age;this.sex = sex;}function Son(uname,age, sex){Father.cal1(this,uname,age, sex);}
apply
apply()方法调用一个函数。
简单理解为调用函数的方式,但是它可以改变函数的this指向。fun.apply(thisArg,[argsArray])
- thisArg :在fun函数运行时指定的this值
- argsArray:传递的值,必须包含在数组(伪数组)里面
- 返回值就是函数的返回值,因为它就是调用函数
应用:利用apply借助数学内置对象求最大值
var arr = [1,66,3,99,4];//var max =Math.max.apply(null,arr);var max = Math.max.apply(Math,arr);
:::warning 相同点:
都可以改变函数内部的this指向.
区别点:
1. call和apply 会调用函数,并且改变函数内部this指向.
2. call和apply传递的参数不一样, call传递参数aru1, aru2..形式 apply必须数组形式[arg]
3. bind不会调用函数,可以改变函数内部this指向.
主要应用场景:
1. call经常做继承.
2. apply经常跟数组有关系.比如借助于数学对象实现数组最大值最小值
3. bind不调用函数,但是还想改变this指向.比如改变定时器内部的this指向. :::严格模式
ES5后新增
- IE10以上才使用
严格模式对正常的JavaScript语义做了一些更改:
1.消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。
2.消除代码运行的一些不安全之处,保证代码运行的安全。
3.提高编译器效率,增加运行速度。
4.禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如: class, enum, export, extends, import, super 不能做变量名
开启严格模式
严格模式可以应用到整个脚本或个别函数中。
因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
为整个脚本、script标签开启严格模式
为函数单独开启严格模式
严格模式中的变化
- 变量名
- 必须先声明在使用
- 不能随意删除已经声明好的变量
- this
- 严格模式下全局作用域中的函数的this是undefined
- 严格模式下如果构造函数不加new调用,this会报错
- new实例化的构造函数指向创建的对象实例
- 计时器中的this还是只想Windows
- 事件对象还是只想调用者
- 函数变化
- 函数中调用的参数名也不能相同
- 函数必须声明在顶层,新版本的JavaScript 会引入“块级作用域”(ES6中已引入)。为了与新版本接轨不允许在非函数的代码块内声明函数。
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
此时fn就是一个高阶函数
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。
最典型的就是作为回调函数。//高阶函数-函数可以作为参数传递function fn(a, b, callback) {console.log(a + b);callback && callback();}fn(1,2,function() {console.log('我是最后调用的);});
闭包
闭包( closure )指有权访问另一个函数作用域中变量的函数。
简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。
作用:延伸了变量的作用范围function fn() {var num = 10;function fun() {console.log(num);}return fun();}var f = fn();f();
打印当前li的索引节点号
利用动态添加属性的方式
var lis = document.queryselector( '.nav' ).querySelectorAl1( 'li ');for (var i = o; i < lis.length; i++){lis[i].index = i;lis[i].onclick = function() {console.log(this.index);}}
利用闭包的方式得到当前的li索引号
```javascript //立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量 for(let i = 0;i < lis.length; i++){ (function(i){ // console.log(i); lis[i].onclick = function() { console.log(i); } })(i); }
<a name="RNvi6"></a>#### 定时器中的闭包```javascriptvar lis = document.querySelector('.nav').querySelectorAl1('li');for (var i = 0; i < lis.length; i++) {(function(i) {setTimeout(function() {console.log(lis[i].innerHTML);},3000)})(i);}
打车
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) {flag ? total + 10 : total;}}})();
思考
var name = "The window";var object = {name: "My Object",getNameFunc: function() {return function() {return this.name;};}};console.1og(object.getNameFunc()())——————————————————————————————————————————————————var f =object.getNameFunc();//类似于var f = function() {return this.name;}f();function (){this}()因此会输出windows下的name,因为var name是定义在全局中的,因此是位于windows下的,因此最终结果输出“The Windows”此时并没有闭包产生,因为没有局部变量
var name = "The Window";var object = {name: "My object",getNameFunc: function() {var tha= this;return function() {return that.name;};}};console.log(object.getNameFunc()());___________________________________________________var f = object.getNameFunc();var f = function() {return that.name;};f();打印结果为My Object
递归
深拷贝 浅拷贝
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用.
深拷贝拷贝多层,每一级别的数据都会拷贝.
var obj = {id: 1,name: 'andy ';msg: {age: 18;}}var o = {};for (var k in obj) {//k是属性名 obj[k]属性值o[k] = obj[k];}
ES6 语法糖实现浅拷贝
深拷贝

function deepCopy ( newobj, oldobj ) {for (var k in oldobj) {//判断我们的属性值属于那种数据类型// 1.获取属性值oldobj[k]var item = oldobj[k];// 2.判断这个值是否是数组if (item instarceof Array) {newobj[k]= [];deepcopy(newobj[k],item)}else if (item instanceof Object) {//3.判断这个值是否是对象newobj[k]={};deepCopy(newobj[k],item)}else{//4.属于简单数据类型newobj[k] = item;}}}
正则表达式
正则表达式(Regular Expression )是用于匹配字符串中字符组合的模式。
在JavaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例验证表单:用户名表单只能输入英文字母、数字或者下划线,昵称输入框中可以输入中文(匹配)。
此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
类必须使用new实例化对象








