课程介绍
大纲
1.原型及作用
2.游戏:贪吃蛇
3.继承
4.高阶函数(内置的方法+正则表达式)
5.正则表达式
目标
- 理解面向对象开发思想
- 掌握Javascript面向对象开发相关模式
- 掌握在Javascript中使用正则表达式
案例演示:贪吃蛇
Day1介绍
1.复习-------------------------------------------大量时间,压缩至30分钟
2.面向对象和面向过程的编程思想
3.对象创建的三种方式
4.原型的引入 ------------------------------------原型的作用
5.原型的写法-------------------------------------重点
5.实例对象和构造函数和原型对象三者的关系---------重点
6.体验面向对象的方式编程的思想----体验案例-------理解
7.原型的简单语法---------------------------------注意的问题
8.随即食物的产生---------------------------------小例子,要求能够写出来
基本概念复习
重新介绍Javascript
- Javascript是什么:
是一门脚本语言, 是一门解释型语言, 是一门弱类型语言, 是一门基于对象的语言, 是一门动态的语言
- Javascript与浏览器的关系
动态页面:
1.页面由Html+css+js组成
2.向服务器发送请求,服务器上没有页面,是通过动态生成返回给客户端
- Javascript的组成
1.ECMAscript标准—基础语法和基本对象
2.DOM Document Object Model 文档对象模型 :描述了处理网页内容的方法和接口
3.BOM Browser Object Model 浏览器对象模型 - Javascript能做什么
Js最初的目的:解决用户和服务器之间的交互问题
现在:js可以做特效,游戏,移动,服务器
基本概念
- 语法
- 区分大小写
- 标识符
- 注释
- 严格模式
- 语句
- 关键字和保留字
- 变量
- 数据类型
- typeof操作符
- Undefined
- Null
- Number
- String
- Object
- 操作符
- 流程控制语句
- 函数
Javascript中的基本数据类型
Javacript中有5中简单数据类型Undefined
、Null
、Boolean
、Number
、String
和一种复杂数据一类型object
- 基本类型(值类型)
- Undefined
- Null
- Boolean
- Number
- String
- 复杂类型(引用类型)
- Object
- Array
- Date
- RegExp
- Function
- 基本包装类型
Boolean
Number
String- 单体内置对象
Global
Math - 类型检测
- typeof -------------------------------检测当前对象的数据类型
- instanceof--------------------------判断当前对象是不是这种数据类型
- obj.protitype.toString.call()-----后续再介绍
- 值类型和引用类型在内存中的存储方式
- 值类型按值存储
- 引用类型按引用存储 : 地址在栈,对象在堆
- 值类型复制和引用类型复制
- 值类型按值复制
- 引用类型按引用复制
- 值类型和引用类型参数传递
- 值类型按值传递,传递的是值
- 引用类型按引用传递,传递的是地址
- 值类型和引用类型差别
- 基本类型在内存中占据固定的空间,因此被保存在栈内存中
- 从一个变量向另一个变量复制基本类型的值,复制的是值的副本
- 引用类型的值是对象,保存在对内
- 引用类型的值是对象,保存在堆内存
- 从一个变量向另一个变量赋值引用类型的值的时候,赋值的是引用指针,因此两个变量最终都指向同一个对象
- 小结
- 值类型检测
- 值类型和引用类型的存储方式
- 值类型复制和引用类型复制
- 方法参数中值类型和引用类型参数传递
Javascript的执行过程
Javascript运行分为两段
- 预解析
- 全局预解析(所有变量和函数生命都会提前,同名的函数和变量函数的优先级高)
- 函数内部预解析(所有的变量、函数和形参都会参与预解析)
- 函数
- 形参
- 普通变量
- 执行
先解析全局作用域,然后执行全局作用域中的代码
在执行全局代码的过程中遇到函数调用就会先进行函数预解析,然后再执行函数内部代码。
Javascript面向对象编程(面向对象介绍 )
什么是对象?万物皆对象
- 对象:特指 的某个事物,具有属性和方法(一组无序的属性的集合)
特征:----->属性
行为:----->方法- 对象是单个事物的抽象
- 对象是一个容器,封装了属性(prototype)和方法(method)。属性是对象的特征,方法是对象的行为
- ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。严格来讲对象是一组没有特定顺序的值。
什么是面向对象
面向对象编程 ----(Object Oriented Programming),简称OOP,是一种编程开发思想
这种思想将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真是世界的模拟。
在面向对象程序开发思想中,每个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一些列函数或指令组成的传统的过程式编程(procedural programming)更适合多人合作的大型软件项目
面向对象与面向过程:都是编程思想
- 面向过程:所有的事情都是亲力亲为,注重的是过程
- 面向对象:提出需求,找对象,对象解决,注重的是结果。 面向对象不是面向过程的代替,而是面向过程的封装。
面向对象的特征
- 封装:就是包装,把一些重用的内容进行包装,在需要的时候直接使用。
eg:把一个值,存在一个变量中; 把一些重用的代码放在函数中;把好多相同功能的函数放在一个对象中;把好多功能的对象放在一个文件中;发一些相同的内容放在一个对象中
- 继承:类与类之间的关系,js中没有类的概念,js中有构造函数的概念,是可以有继承的,是基于原型的。
- [多态] :同一个行为,针对不同的对象产生了不同的效果。
- (抽象性) js中一般不谈
扩展阅读:维基百科-面向对象程序设计
面向对象的编程思想
根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,把行为变成方法,然后定义(js)的构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求,这就是面向对象的编程思想
程序中面向对象的基本体现
在Javascript中,所有数据类型都可以视为对象,当然也可以自定义对象
内置对象:js自带的对象
自定义对象:自己定义的对象
我们可以通过自定义的对象来模拟
面向对象的思想是从自然界中来的:类(class)和实例(instance)
class是一种很抽象的概念;在js中new构造函数就相当于类
实例则是一个个具体的;而js中new构造函数得出的结果给一个变量,此时这个变量相当于实例对象
所以,面向对象的设计思想是:
- 抽象出类class
- 根据类class 创建实例化对象instance
- 指挥实例instance得出结果
- 面向对象的抽象过程又比函数要高,因为一个class既包含数据,又包含操作数据的方法。
创建对象------->实例化对象
对象:特指 的某个事物,具有属性和方法(一组无序的属性的集合)
特征:----->属性
行为:----->方法
具体的对象抽象成类,通过类的属性和方法实例化为一个具体的对象
- 三种创建对象的方式
- 字面量的方式
- 调用系统的构造函数
- 自定义构造函数方式
工厂模式和自定义构造函数的区别
-
共同点:都是函数,都可以创建对象,都可以传入参数
-
不同点:
工厂模式: 自定义构造函数:(平时常用) 函数名首字母是小写 函数名首字母是大写 没有new 没有new 有返回值 没有返回值 new之后的对象时当前的对象 this是当前的对象 直接调用函数就可以创建对象 通过new的方式来创建对象
如何判断对象是不是某个数据类型?
- (1)通过构造器的方式 实例对象.构造器==构造函数名字
- (2)对象 instanceof 构造函数名字
推荐使用第二种方式来识别,原因等学完原型再说。
体会面向过程和面向对象的编程思想
原型
构造函数创建对象带来的问题(原型的引入)
原型定义
- 实例对象中有__proto__这个属性,叫原型,也是一个对象,这个属性是给浏览器使用的,不是标准属性. proto 可以叫原型对象
构造函数中有prototype这个属性,叫原型,也是一个对象,这个属性是给程序员使用的,是标准的属性. prototype 可以叫原型对象 - 图解说明
- 实例对象的__proto__和构造函数中的prototype相等—>true
- 又因为实例对象是通过构造函数来创建的,构造函数中有原型对象prototype
- 实例对象的__proto__指向了构造函数的原型对象prototype
构造函数和实例对象和原型对象之间的关系
1.构造函数可以实例化对象
2.构造函数中有一个属性叫prorotype,是构造函数的原型对象
3.构造函数的原型对象prorotype中有一个constructor构造器,这个构造器指向就是,自己所在的原型对象,所在的构造函数
4.实例对象的原型对象__proto__指向的是该构造函数创建的原型对象prototype
5.构造函数的原型对象prototype中的方法是可以被实例对象直接访问的
利用原型共享数据(共享属性和方法)
- 原型添加方法解决数据共享
如果想要使用一些属性和方法,并且属性的值在每个对象中都是一样的,方法在每个对象中的操作也都是一样的,那么为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值。
- 原型的作用之一:数据共享,节省空间
- 不需要共享的数据写在构造函数中,需要共享的数据写在原型中
- 原型对象中添加的方法 是可以相互访问的
- 实例对象使用的属性和方法层层的搜索
实例对象使用的属性/方法,先在实例对象中查找,找到了则直接使用;找不到则去实例对象(所在的构造函数的原型对象)的__proto__指向的原型对象prototype中 去找,找到直接使用,找不到就报错.
- 为内置对象的原型对象中添加方法
简单的原型语法
局部变量变成全局变量
浏览器中的顶级对象是window
把局部变量给window下面的一个属性即可实现将局部变量变成全局变量
- 图解说明:
window是对象,有2个空间(左右)
window指向右边的空间
对象之间传递给地址
win 也有自己的空间 ,指向右边的空间,多了一个num属性
所以window访问时也能读取右边的num属性
产生随机数对象
把随机数对象暴露给window成为全局对象
案例随机小方块(贪吃蛇的食物)
Day2介绍
1.案例—贪吃蛇-------------面向对象思想
2.原型添加方法的练习--------熟练
3.私有的函数----------------外部不能访问,自己访问
4.陌生的方法bind(对象)----直接用,大概解释一下
5.封装三个对象
* 面向对象的编程思想:
根据需求,抽象出相关的对象,总结对象的特征和行为,把特征变成属性,把行为变成方法,然后定义(js)的构造函数,实例化对象,通过对象调用属性和方法,完成相应的需求,这就是面向对象的编程思想
贪吃蛇编程思想
- 1.地图:宽/高/背景颜色.因为小蛇和食物都是相对于地图显示的,所以食物和蛇都是地图的子元素;因为都要随即位置显示,所以要脱离文档流,地图也需要脱离文档流—>css需要设置宽/高/背景颜色,脱标releative
- 2.食物:宽/高/背景颜色/横坐标/纵坐标.
食物------->div元素
elements—>存储div的数组(将来删除食物div的时候,先从map中删除div元素,再从数组中移除div)
一个食物就是一个对象,这个对象有相应的属性,这个对象需要在地图上显示
最终要创建食物的对象,先有构造函数,并且把相应的值作为参数传入到构造函数中
食物要想显示在地图上,食物的初始化就是一个行为
1.食物的构造函数 ----->目的是创建对象
2.食物的显示的方法 ----->目的通过对象调用方法,显示食物,设置相应的样式
2.1.1因为食物要被小蛇吃掉,吃掉后应该再次出现失误,原来的食物就删除了
2.1.2每一次初始化食物的时候先删除原来的食物,然后重新的初始化食物
2.1.3通过一个私有的函数(外面不能调用的函数)删除地图上的食物,同时最开始的时候食物也相应的保存到了一个数组中,在从这个数组中把食物删除
3.最后把食物的构造函数给window下的属性,这样,外部就可以直接使用这个食物的构造函数了 (window.Food=Food;) - 3.小蛇
小蛇就是一个对象
属性:每个身体都有宽,高,颜色
属性:身体分三个部分,每个部分都是一个对象,每个部分都有横纵坐标,背景颜色
小蛇要想显示在地图上,先删除之前的小蛇,再初始化小蛇(小蛇要移动)—方法
3.1小蛇要移动—方法
思路:把小蛇头的坐标给小蛇第一部分的身体,第一部分的身体的坐标给下一部分的身体
小蛇的头:需要单独设置:方向 - 4.游戏
- 5.页面实例化对象并执行初始化
贪吃蛇
- 页面的任何位置 ,按下键盘,获取按键的值
复习
day1总结
day2总结
Day 3介绍
重点:
1.原型链
2.不同的继承
3.原型的另外一个作业:继承
4.this指向要知道到底是谁
复习原型
原型链
原型的指向是否可以改变
继承
如何实现继承?
原型的方式继承
借用构造函数继承
组合继承
拷贝继承
函数的不同表现方式
函数的调用的不同方式
this指向
严格模式
函数也是对象–对象不一定是函数
数组中的函数如何调用
apply和call讲解
原型及原型链
- 通过原型添加属性方法
如果想要使用一些属性和方法,并且属性的值在每个对象中都是一眼不过的,方法在每个对象中的操作也都是一样的,那么为了共享数据,节省内存空间,是可以把属性和方法通过原型的方式进行赋值。
-
原型:
实例对象中有__proto__这个属性,叫原型,也是一个对象,这个属性是给浏览器使用的,不是标准属性. proto 可以叫原型对象
构造函数中有prototype这个属性,叫原型,也是一个对象,这个属性是给程序员使用的,是标准的属性. prototype 可以叫原型对象 -
原型链:实例对象和原型对象之间的关系是通过__proto原型来联系起来的,这个关系就是原型链
-
原型的指向是可以改变的
实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象
构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(proto)指向也会发生改变
-
原型最终指向了哪里:原型链最终的指向是Object的prototype中的__proto__是null
对象中的__proto__指向的是构造函数的prototype,
所以,prototype这个对象中__proto__指向的应该是某个构造函数的原型prototype
per实例对象的__proto__原型—>指向构造函数原型的原型Person.prototype的__proto__—>指向Object的原型对象
Object的原型对象prototype的__proto__指向的是null
任何的函数里面的__proto__原型指向的都是Object的原型对象
- 原型指向改变如何添加方法和访问:
如果原型指向改变了,就应该在原型改变之后添加原型方法>
因为,当先添加原型方法时候,方法被添加到了Student原型中去了;此时改变原型对象的指向,原型对象就会放弃现在的指向,重新指向Person的原型,从而无法调用Student原型中的方法
然而,当先改变原型对象的指向时,原型已经指向Persond的实例对象,此时再添加圆形方法就加入到了Person原型中去了,所以可以访问sayHi();方法
- 原型指向改变的3种方法
//改变原型指向的方法一
Person.prototype= new Student();
//改变原型指向的方法二
var stu=new Student();
Person.prototype=stu;
//改变原型指向的方法三
Person.prototype={...};
- 实例对象的属性和原型对象中的属性重名问题
- 问题1:实例对象访问这个属性,应该先从实例对象中找,找到了就直接用,找不到就去指向的原型对象中找,找到了就用,找不到呢?===》找不到就报undefined,而不会报错。因为js是一门动态类型的语言,对象没有什么属性,只要对象.属性名,点了,那么这个对象就有了这个属性,但是该属性没有赋值,所以结果是undefined
- 问题2:通过实例对象能否改变原型对象中的属性值?===》不能 per.sex=“植物”,实例对象的属性已经从男—>植物,原型对象__proto__里面还是女
- 问题3:就想改变原型对象中属性的值?===》可以改变:直接通过原型对象.属性=值;Person.prototype.sex=“不男不女”;
继承引入
-
面向对象编程思想:根据需求,分析对象,找到对象有什么(共同的)特征和行为,通过代码的形式来实现需求;要想实现这个需求就要创建对象,要想创建对象,就应该先有构造函数,然后通过构造函数来创建对象,通过对象调用属性和方法来实现相应的功能及需求,即可。
-
首先js不是一门面向对象的语言,JS是一门基于对象的语言,为什么还要学习js还要学习面向对象?因为面向对象的思想适合于人的想法,编程起来会更加的方便,及后期的维护。
-
面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是js不是面向对象的语言,所以js中没有类(class)的概念,但是Js可以模拟面向对象的思想编程,Js中会通过构造函数来模拟类(class)的概念
-
面向对象的特性:封装,继承,多态
- 封装:就是包装
- 继承:首先继承是一种关系,类与类之间的关系,js中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承。
继承也是为了实现数据共享,js中继承也是为了实现数据共享
原型的作用:1.数据共享,节省内存空间;2.为了实现继承
继承是一种关系,父类级别与类级别的关系,类可以继承父类的属性和方法
- 多态:一个对象有不同的行为,或者是同一个行为针对不同的对象产生不同的结果,要想有多态,就要先有继承,js中可以模拟多态,但是不会去使用,也不会模拟,因为这样的后果会导致数据不能共享,占用大量的空间
继承
首先继承是一种关系,类与类之间的关系,js中没有类,但是可以通过构造函数模拟类,然后通过原型来实现继承。继承是一种父类级别与类级别的关系,类可以继承父类的属性和方法。原型有2个作用:1.数据共享,节省内存空间;2.为了实现继承。继承也是为了实现数据共享。
通过原型来实现继承
(缺陷:直接初始化了属性,继承过来的属性值都是一模一样的了)
改变原型指向继承案例
动物:name,weight,eat();
狗:name,weight,color,eat();bite();
哈士奇:name,weight,color,sex,eat();bite();happy();
借用构造函数继承
(缺陷:父级类别中的方法不能调用)
通过改变原型指向实现的继承:为了共享数据,改变原型指向,实现了继承。
缺陷:因为改变原型指向的同时实现继承,直接初始化了属性,继承过来的属性值都是一模一样的了,只能他重新调用对象的属性进行赋值才能改变重复的属性值问题(更麻烦,放弃)
解决方案:继承的时候不用改变原型的指向,直接调用父级的构造函数的方式来为属性赋值就可以了—借用构造函数:把要继承的父级的构造函数拿过来,试用一下就可以了
借用构造函数:构造函数名字.call(当前对象(this),属性,属性,属性,属性…);
解决了属性继承,并且属性值不重复的问题
缺陷:父级类别中的方法不能调用,组合继承能解决这种问题
组合继承
属性和方法都被继承了
拷贝继承
把一个对象中的属性或者方法通过循环遍历的方式放到另外一个对象当中,就是拷贝继承
总结继承:
面向对象的特性:封装,继承,多态
继承,类与类之间的关系,面向对象的语言的继承是为了多态服务的
js不是一门面向对象的语言,但是可以模拟面向对象。模拟继承。为了节省内存空间
继承:
原型的作用1:数据共享;目的是:为了节省内存空间
原型的作用2:继承 ;目的是:为了节省内存空间
1.原型继承:改变原型指向(无法解决属性重复)
2.借用构造函数继承:主要解决属性重复的问题(无法调用父级类别中的方法)
3.组合继承:原型jicheng+借用函数继承。通过父级类.call(this,属性,属性...)方法
既能解决属性问题,又能解决方法问题
4.拷贝继承:就是把对象中需要共享的属性或者方法,直接遍历的方式复制到另一个对象中
如果用构造函数的方式创建对象,优先选择组合继承
- 逆推继承看原型
逐个向上查找age,30-20-10,如果都没有,就是undefined。
函数进阶
函数的角色(函数的定义方式):函数声明和函数表达式
- 函数的角色
- 函数的声明
- 函数表达式
- 二者的区别:
函数声明如果放在if-else的语句中,在IE8浏览器中会出现问题
在函数预解析里面,所有声明提前,
所以谷歌浏览器会打印 “这是函数声明”
而在IE8浏览器中会打印 “这ye是函数声明”
在谷歌和IE8中均打印 “这是函数声明”
以后宁愿用函数表达式,都不要用函数声明
函数中this的指向问题
函数中的this指向
普通函数中的this是谁?—window
对象.方法中的this是谁?-------该方法所属对象
定时器中的this是谁?—window
构造函数中的this是谁?—当前的实例对象
原型对象方法中的this是谁?—实例对象
函数的调用方式
- 普通函数 f1();
- 构造函数 var f=new F1();
- 对象方法 per.play();
函数也是对象
所有的函数实际上都是Function 的构造函数创建出来的实例对象
数组中函数的调用
复习(原型+原型的作用+继承+原型链+函数中的this的指向+创建对象的的三种方式+面向对象和面向过程都是编程思想)
原型:
每个实例对象中都有一个属性 __proto__,是原型,浏览器使用的,不标准的属性
每个构造函数中都有一个属性 prototype,是原型,程序员使用
原型的指向是可以改变的,所以js中是通过改变原型来实现继承的
原型的作用:
实现数据共享;继承。目的都是为了节省内存空间.
如果属性和方法都需要共享,那么就把属性和方法添加到原型中。
面向对象和面向过程都是编程思想:
面向对象注重的是结果,面向过程注重的是过程
面向对象的特性:封装,继承多态
继承:
1.通过原型实现继承,改变原型的指向,属性在初始化的时候就已经固定了,如果实际多个对象实例化,那么每个实例对象的属性的值在初始化的时候都是一样的
2.借用构造函数实现继承,不能继承方法
3.组合继承,可以解决属性和方法的继承问题
4.拷贝继承,就是把一个对象中的原型中的所有的属性和方法复制一份给另一个对象
创建对象的的三种方式:
1.字面量的方式
2.调用系统的构造函数
3.自定义构造函数
原型链:实例对象和原型对象之间的关系,主要是通过 __proto__ 和prototype 来联系
函数中的this的指向
1.普通函数中的this是window
2.构造函数中的this是实例对象
构造函数一般都是创建实例对象使用的,是通过new关键字,构造函数也是函数
3.方法中的this也是实例对象
4.原型中的方法中的this也是实例对象
函数是对象,构造函数也是函数,所以构造函数也是对象;对象不一定是函数
对象中有__proto__;函数中有prototype ; Math是对象,但不是函数
Day 4介绍
apply和call方法的使用 -----重点
bind方法的使用 ------------重点
函数中的几个成员的介绍-----了解
高阶函数(函数作为参数,函数作为返回值)
函数作为参数使用的案例
函数作为返回值使用的案例
作用域,作用域链,预解析
闭包-----------------------重点,作用,优点,缺点
闭包的案例-----------------重点,要会写
沙箱-----------------------概念,作用,以后该怎么办
递归-----------------------重点,难,难在应用,为了遍历
apply call bind 方法
- apply 和call使用方法
apply 使用语法
函数名.apply(对象,[参数1,参数2,....]);
方法名.apply(对象,[参数1,参数2,....]);
call 使用语法
函数名.call(对象,参数1,参数2,....);
方法名.call(对象,参数1,参数2,....);
作用:改变this的指向(函数中和方法中都可以)
不同:参数传递的方式不同
apply和call都可以让函数或者方法来调用,传入参数和函数自己调用的写法不一样,但是效果是一样的
如果想要使用其他方法,就用 ** 其他对象.方法名.apply(当前对象,[参数1,参数2,....]);** 这个方法就会被当前对象所使用,同时这个方法中的this就是当前对象。在调用方法的时候改变了this的指向。call的用法也一样,就是传参的时候不需要加中括号
注意:
有返回值时,用一个变量接收函数/方法调用的返回值
apply和call方法中如果没有传入参数,或者传入的是null,那么调用该方法的函数对象中的this就是默认的window
apply和call实际上并不在函数这个实例对象中,而是在Function的 prototype中
所有的函数都是Function的实例对象
- bind 方法
bind方法是复制一个方法或者函数,是在复制的同时改变了this的指向
参数可以在复制的时候传进去,也可以在之后调用的时候传入进去
apply和call是调用的时候改变this指向
bind方法是复制一份的时候改变了this的指向
bind 使用语法
函数名.bind(对象,参数1,参数1,...); ---->返回值是复制之后的函数
方法名.bind(对象,参数1,参数2,....); ---->返回值是复制之后的方法
- bind的应用
函数 高阶函数
- 函数中的几个成员
函数中有一个name属性,—>存储的是函数的名字,name是只读属性不能修改
函数中有一个arguments属性,—>获取的是实参的数组。arguments.length获取的是实参的个数
函数中有一个length属性,—>函数定义时形参的个数
函数中有一个caller属性,---->是调用者,f1函数在f2函数中被调用的,此时调用者就是f2
函数作为参数
- 函数作为参数的时候,如果是命名函数,那么只传入命名函数的名字,没有写括号
- 定时器中传入参数
- 案例:函数作为参数:排序
sort参考MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
函数做为返回值
- 获取某个对象的数据类型的样子(因为Object是本身就有的所以不需要调用call,其他方法都得调用call)
Object.prototype.toString.call(对象);//此时得到的就是这个对象的类型的样子
- 案例:函数作为返回值使用 :排序
作用域和作用域链及预解析
- 作用域
变量----->局部变量和全局变量
作用域:就是变量的使用范围。局部作用域和全局作用域
js中没有块级作用域名----->一对大括号中定义的变量,这个变量可以在大括号外面使用
函数中定义的变量是局部变量
- 作用域链
作用域链 :变量的使用,从里向外,层层搜索,搜索到了就可以直接使用
层层搜索,搜索到0级作用域,如果还是没有找到这个变量,结果就是报错
- 预解析:就是在浏览器解析代码之前,把变量的声明和函数的声明提前(提升)到该作用域的最上面。
闭包
- 闭包的概念:
定义一:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者数据,此时形成了闭包(此定义暂时不严谨)
定义二:函数中有另一个函数,或者是函数中有另一个对象,里面的函数或者是对象都可以使用外面函数中定义的变量或者数据,此时形成闭包(这个被使用的数据不一定是变量也有可能是参数也是可以的) (推荐) - 闭包的模式:
函数模式的闭包:在一个函数中有一个函数,里面函数可以访问上层函数中的变量(局部变量)
对象模式的闭包:函数中有一个对象,该对象可以访问函数中的变量(局部变量)
- 闭包的作用:缓存数据,延长作用域链
- 闭包的优点和缺点:延长了作用域链,使用缓存的数据不及时的被释放。
局部变量是在函数中,函数使用结束后,局部变量会被自动的释放
闭包后,里面的局部变量的使用作用域链就会被延长 - 闭包的应用:如果想要缓存数据,就把数据放在外层的函数和里层的函数的中间位置
沙箱
- 沙箱:就是一个环境,也可以叫黑盒,在这个环境中模拟外面真实的开发环境,完成需求,效果和外面真实的开发环境是一样的。
- 沙箱格式:函数的自调用两种写法都称为沙箱
(function(){…}());
(function(){…})(); 推荐使用这种格式 - 沙箱的作用:避免命名冲突。
沙箱中的变量和全局变量不冲突,自调用函数中的所有代码执行的都是沙箱里面定义的变量。 这样写出来的代码是绝对不会和外面其他的代码发生冲突的,为了避免冲突,可以将变量放到自调用函数中即沙箱中写。
- 沙箱使每段代码都放在自己的独立环境中不会与全局变量产生冲突
- 沙箱案例操作页面元素
递归
-
递归:函数中调用函数自己,此时就是递归,递归有一定要有结束的条件,否则就是死循环。
-
递归应用:一般应用在遍历上
-
递归轻易不要使用,效率很低(一般递归+闭包/沙箱可以进行优化,比较难理解,此处暂时不学)
-
分析上面递归案例中的代码执行过程
执行过程
代码执行 getSum(5)—>进入函数,此时x是5,执行的是5+getSum(4),此时代码等待,先不计算,先执行getSum(4),进入函数,执行的是4+getSum(3),等待,先执行的是getSum(3),进入函数,执行3+getSum(2),等待,先执行getSum(2),进入函数,执行2+getSum(1),等待,先执行getSum(1),执行的是x==1的判断,return 1,所以,
此时的getSum(1)的结果是1.此时开始向外走出去.
2+getSum(1) 的结果是2+1
执行3+getSum(2)的结果是3+2+1
执行4+getSum(3)的结果是4+3+2+1
执行5+getSum(4)的结果是5+4+3+2+1
所以最终结果是n=5—>5+4+3+2+1=15 -
递归案例(“求一个数字各个位数上的数字的和”、“斐波那契数列”)
Day 5介绍
浅拷贝
深拷贝-------||---------->递归
遍历DOM树----||------>递归------最好能写出来
正则表达式-----------------很重要
元字符
写几个正则表达式
写代码
正则表达式的案例7-8个==========>代码写出来
数组和伪数组的区别
浅拷贝和深拷贝及遍历DOM树
- 浅拷贝
浅拷贝:写一个函数把一个对象的属性复制到另一个对象中
拷贝就是复制,相当于把一个对象中的所有内容,复制一份给另一个对象,直接复制或者说,就是把一个对象的地址给了另一个对象,他们指向相同,两个对象之间有共同的属性和方法,都可以使用。
一个对象是很多个无序属性的集合,想要复制对象中的所有属性,需要遍历这个对象。
把a对象中的所有属性复制到对象b中:因为 obj[“name”] <===> obj.name 两种写法是一样的;所以b[key]=a[key]; 的意思就是将a对象的属性及属性值复制给b对象
- 深拷贝
深拷贝:拷贝还是复制,深:把一个对象中所有的属性或者方法,一个一个的找到,并且在另一个对象中开辟相应的空间,一个一个的存储到另一个对象中
一般不会使用,因为效率更低。
- 遍历DOM树
节点有三个属性:nodeName,nodeType,nodeValue
遍历DOM树思路:
第一个函数: 给我根节点,显示的是根节点的名字 :forDOM(根节点)
获取根节点中的所有子节点
var children = 根节点.children;
调用第二个函数
第二个函数:给我所有的子节点,我把每个子节点的名字全部显示出来(children)
for(var i=0;i<children.length;i++){
每个子节点
var child=children[i];
f1(child); 给我节点,我显示该节点的名字
child是子节点,但是如果child里面还有子节点,此时child就是爹了
child.children&&第一个函数(child);
}
正则表达式 基本元字符和限定字符
- 正则表达式:也叫规则表达式,按照一定的规则组成的一个表达式.在大多数编程语言中都可以使用。(英语:Regular Expression,在代码中常简写为regex、regexp或RE)
- 正则表达式的作用:匹配字符串的
- 正则表达式组成:由元字符或者是限定符组成的一个式子
- 正则表达式参考资料:请以MDN官方为准
百度:该网址中符号中的元字符就是的 https://baike.baidu.com/item/正则表达式/1700215?fr=aladdin
MDN:该网址中4.1正则表达式特殊字符就是元字符。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
- 元字符
. 表示任意一个除了 \n 之外的任意一个字符。
[ ] 表示的是:范围。
[0-9] 表示的是0到9之间的任意一个数字。
[100-200] 这种就是错误的,应该写成:[1][0-9][0-9],只能表示一位数。
[a-z] 表示的是:所有的小写字母a-z中的任意的一个
[A-Z]表示的是:所有的大写字母A-Z中的任意的一个
[a-zA-z] 表示的是:所有的字母中的任意的一个
[0-9a-zA-Z]表示的是:所有的数字或者是字母中的任意的一个
[ ] 另一个含义:把正则表达式中元字符的意义干掉。[.] 就仅仅表示一个点,不再具有正则意义
| 或者 的意思
[0-9]|[a-z] 表示的是要么是一个数字,要么是一个小写字母
或的优先级非常低,最后才计算或 |
() 含义:1.改变优先级 2.提取组(分组)。
[0-9]|([a-z])|[A-Z] ------->提升优先级
([0-9])(a-z)(1-5) 三组,从最左边开始计算
(()(())) 共四组。从最左边的小括号开始计算,有几个"(" 表示有几组
- 限定符:限定前面的表达式出现的次数。限定符也是元字符。
* 表示的是:前面的表达式出现了0到多次
[a-z][0-9]* 表示: 小写字母中的任意一个 后面是要么没有数字的,要么是多个数字的
"sdfb55555" 可以匹配 [a-z][0-9]*
+ 表示的是:前面表达式出现了1次或多次。
[a-z][9]+ 表示:小写字母一个后面最少一个9,或者多个9。
"sdfbg9hssjjk6" 可以匹配 [a-z][9]+
? 表示的是:前面的表达式出现了0次或1次,最少是0次,最多是1次。
"58694ihjb" 可以匹配 [4][a-z]?
?的另一个意思是:阻止贪婪模式 。目前用不到。
^ 表示的是以什么什么开始,或者是取非(取反)
^[0-9] 表示以数字开头
^[a-z] 表示以小写字母开始
[^0-9] 取反 表示非数字
[^a-z ] 取反 表示非小写字母
[^0-9a-zA-Z] 取反 表示除了数字和字母意外的其他符号
$ 表示的是以什么什么结束
[0-9][a-z]$ 必须以小写字母结束
[0-9][a-z]$ 可以匹配 "f3241hagv"
^[aaa][bbb] $ 相当于严格模式 必须以aaa开始bbb结束,只能匹配aaabbb
^[0-9][a-z]$ 只能匹配"4f"这种一个数字换个一个小写字母的 而不能匹配 "f3241hagv"。
\d 表示任意一个数字<==> [0-9]
\D 表示任意一个非数字<==>[^0-9]
\s 表示任意一个空白符 (space tab)
\S 表示非空白符号 \s的反面
\w 表示非特殊符号 等价于 [a-zA-z0-9_] (下划线是非特殊符号)
\W 表示特殊符号 等价于 [^a-zA-z0-9_]
\b 单词边界
\ 反斜杠表示转义
\t 匹配一个水平制表符
\f 匹配一个换页符
\n 匹配一个换行符
\r 匹配一个回车符
{ } 更加明确前面的表达式出现的次数
{0,}表示前面的表达式出现了0到多次。和 * 是一样的效果。
{1,} 表示前面表达式出现了1次或多次。和 + 是一样的效果。
{0,1}表示前面的表达式出现了0次到1次。和 ? 是一样的效果。
{,10}是错误的===============不能这么写,应该写{0,10}
{n} 表示该限定符前面的表达式出现了n次。
{n,} 表示前面的表达式出现了n到多次。
{n,m} 表示前面的表达式至少出现n次,最多出现m次 。
正则表达式练习
- 身份证号码
<script>
// 写正则表达式,根据字符串来写正则表达式进行匹配
// 经验 :1.找规律 , 2.不要追求完美
// 身份证的正则表达式: 15位或者18位
([1-9][0-9]{14})|([1-9][0-9]{16}[0-9xX])
// 简写
([1-9][0-9][14])([0-9]{2}[0-9xX])? 前面括号里表示15位,再接上3位,后面?表示0-1,取1时表示18位
</script>
- 座机号码的正则表达式(010-19876754 0431-87123490)
<script>
//座机号码的正则表达式
//010-19876754
//0431-87123490
[0-9]{3,4}[-][0-9]{8}
// 简写
\d{3,4}[-]\d{8}
</script>
- qq号码的正则表达式
<script>
//qq号码的正则表达式
[1-9][0-9]{4,10}
\d{5,11}
</script>
- 手机号码的正则表达式
<script>
0//手机号码的正则表达式
//130-139
//143 147
//150-159
//170 171 173 176 177
//180-189
([1][358][0-9]{9})|([1][4][37][0-9]{8})|([1][7][01367][0-9]{8})
\d{11}
</script>
- 邮箱的正则表达式.记住
//邮箱的正则表达式.记住
//字母或数字或带_或带.或带-,中划线必须放在最后,否则就需要转移 @ .com
//as123_.sh@jbkljb.com.cn
//[0-9a-zA-z_.-]+ :+表示多个字符. ([.][a-zA-Z]+){1,2}:表示一级域名或二级域名.
[0-9a-zA-z_.-]+[@][0-9a-zA-z_.-]+([.][a-zA-Z]+){1,2}
创建正则表达式
创建正则表达式两种方式:
1.通过构造函数创建对象
2.通过字面量的方式创建对象
正则表达式的作用:匹配字符串
创建正则表达式的步骤:
1.创建正则表达式对象
2.调用正则表达式的方法,将需要匹配的字符串放进去,
3.返回值为布尔类型,匹配位返回true不匹配返回false,可用变量接收打印查看
- 识别正则表达式的匹配结果
![识别正则表达式匹配结果]
正则表达式的案例
- 验证密码强弱
<script>
/*
密码:数字,字母,特殊符号
只有数字/字母/特殊符号----1级,弱
两两组合----2级,中
三者都有----3级,强
*/
//定义 my$(),写在common.js中
function my$(id){
return document.getElementById(id);
}
//获取文本框,注册键盘抬起事件
my$("pwd").onkeyup=function(){
//每次键盘抬起都要获取文本框中内容,验证文本框中有什么东西,得到一个级别,下面的div显示对应的颜色
//如果密码的长度小于6,没必要判断
// if(this.value.length>=6){
// var lvl=getLvl(this.value);
// my$("strengthLevel").className="strengthLv"+lvl;
// }else{
// my$("strengthLevel").className="strengthLv0";
// }
// 简写用三目运算符判断
my$("strengthLevel").className="strengthLv"+(this.value.length>=6 ? getLvl(this.value):0);
};
//给我密码,我返回对应的级别
function getLvl(pwd){
// 密码中是否有数字,或者字母,或者是特殊符号
var lvl=0; //默认0
if(/[0-9]/.test(pwd)){
lvl++;
}
//判断密码中有没有字母
if(/[a-zA-Z]/.test(pwd)){
lvl++;
}
//判断密码中有没有特殊符号
if(/[^0-9a-zA-Z_]/.test(pwd)){
lvl++;
}
return lvl;//1 2 3
}
</script>
- 验证邮箱
<script>
/*
如果输入的是邮箱,文本框的边框颜色为绿色,否则为红色
*/
// 定义my$
function my$(id){
return document.getElementById(id);
}
//获取文本框,注册失去焦点的事件
my$("email").onblur = function(){
// 判断这个文本框中输入的是不是邮箱(严格模式)
var reg=/^[0-9a-zA-Z_.-]+[@][0-9a-zA-Z_.*]+(.[a-zA-Z]+){1,2}$/;
if(reg.test(this.value)){
this.style.borderColor="green";
}else{
this.style.borderColor="red";
}
};
</script>
- 验证中文名字
<script>
/*
如果输入的是中文,文本框的背景颜色为绿色,否则为红色
中文的正则表达式:[\u4e00-\u9fa5] 代表中文从"一" 到 "龥" 的UniCode编码。
在console 中输入 escap("中文内容") 可以将中文正则翻译为UniCode编码。 eg: escape("作业"); ------------> "%u4F5C%u4E1A"
在console 中输入 unescap("UniCode编码") 可以将UniCode编码翻译成中文。 eg: unescape("%u4F5C%u4E1A") ----------> "作业"
*/
// 获取文本框,注册失去焦点事件
document.getElementById("username").onblur=function(){
var reg=/^[\u4e00-\u9fa5]{2,6}$/; //规定中文名字数需要在2-6个字之间
if(reg.test(this.value)){
this.style.backgroundColor="green";
}else{
this.style.backgroundColor="red";
}
};
</script>
- 表单验证
表单验证布局
<div class="container" id="dv">
<label for="qq">QQ</label><input type="text" id="qq"><span></span><br>
<label for="phone">手机</label><input type="text" id="phone"><span></span><br>
<label for="email">邮箱</label><input type="text" id="email"><span></span><br>
<label for="telephone">座机</label><input type="text" id="telephone"><span></span><br>
<label for="fullname">姓名</label><input type="text" id="fullname"><span></span><br>
<label for="idcard">身份证号</label><input type="text" id="idcard"><span></span><br>
</div>
<script>
// 表单验证js
//定义 my$(),写在common.js中
function my$(id){
return document.getElementById(id);
}
// qq号
checkInput(my$("qq"),/^\d{5,11}$/);
// 手机号
checkInput(my$("phone"),/^\d{11}$/); // 或者 [1][0-9]{10}
// 邮箱
checkInput(my$("email"),/^[0-9a-zA-Z_.-]+[@][0-9a-zA-Z_._]+([.][a-zA-Z]+){1,2}$/);
// 座机
checkInput(my$("telephone"),/^\d{3,4}[-]\d{7,8}$/);
// 姓名
checkInput(my$("fullname"),/^[\u4e00-\u9fa5]{2,6}$/);
// 身份证号
checkInput(my$("idcard"),/^([1-9][0-9]{14})([0-9]{2}[0-9xX])$/);
// 给我文本框,给我与文本框对应的正则表达式,我把结果显示出来(2个参数)
// 通过正则表达式验证当前的文本框是否匹配,并显示结果
function checkInput(input,reg){
// 文本框失去焦点的事件
input.onblur=function(){
if(reg.test(this.value)){
this.nextElementSibling.innerText="格式正确";
this.nextElementSibling.style.color="green";
}else{
this.nextElementSibling.innerText="格式错误,请重新输入";
this.nextElementSibling.style.color="red";
}
};
}
</script>
正则提取
字符串有个方法match的参数只有一个(regexp正则表达式),返回值是一个数组
正则表达式中:g 表示的时全局模式匹配
正则表达式中()作为分组来使用,获取分组匹配到的结果用RegExp.$1 $2 $3…来获取
// 1.提取字符串中所有的数字
var str1 ="中国移动:10086;中国联通:10010;中国电信:10000;"
var array = str1.match(/\d{5}/g);
console.log(array); // --->数组中有3个数据,分别是2个号码
// 2.提取页面所有邮箱号码
var str2 ="saf@hg.com.cn,中国联通,hh@163.com,中国联通,fdg@126.com";
var array2 = str2.match(/[0-9a-zA-Z_.-]+[@][0-9a-zA-Z_.*]+(.[a-zA-Z]+){1,2}/g);
console.log(array2); // --->数组中有3个数据,分别是3个邮箱
// 3.提取日期中的日
var str3 = "2018-11-13";
var array3 = str3.match(/(\d{4})[-](\d{2})[-](\d{2})/g);
// 正则表达式对象.$3获取第3组的数字
console.log(RegExp.$3); //提取组的意思 // --->打印匹配到的第三组数据日:13
// 4.提取邮箱用户名,域名,126
var str4 = "hjfvjhcdhcx@126.com.cn";
str4.match(/([0-9a-zA-Z_.-]+)[@]([0-9a-zA-Z_-]+)(([.][a-zA-Z]+){1,2})/);
console.log(RegExp.$1); //用户名
console.log(RegExp.$2); //126
console.log(RegExp.$3); //域名
正则替换
方法:replace(正则把表达式/字符串,字符串/函数),返回值是字符串
方法trim() 是去掉字符串左右和右边的空格,中间的空格去不掉
正则表达式中: i 表示忽略大小写
用正则表达式对象的方式:将正则表达式放到了构造函数的括号里
// 1.替换字符串中的汉字(可以不用unicode编码)
var str1 ="中国移动:10086;中国联通:10010;中国电信:10000;"
str1=str1.replace(/中国/g,"天朝"); // 不加g只会替换第一个,加g会全部替换
console.log(str1);
// 2.替换空白符,第二个参数不填写任何
var str2 = " 哈哈哈 ,你好搞笑... ";
// str2 = str2.trim();
// console.log("=="+str2+"=="); //只能去掉字符串左右和右边的空格,中间的空格去不掉
str2=str2.replace(/\s+/g,"");
console.log("==="+str2+"===");
// 3.所有的H都替换成S
var str3="HkhgkHkjfh";
str3=str3.replace(/[h]/gi,"S"); //i 表示忽略大小写,g表示全局
console.log(str3);
// 4.换种写法:用正则表达式对象的方式
var reg= new RegExp(/[h]/gi);
var str4="HkhgkHkjfh";
str4=str4.replace(reg,"S");
console.log(str4);
数组和伪数组
真数组和伪数组的区别
1.length
真数组有length,且长度是可随着添加元素改变的
伪数组没有length,即便写入length也不会随着添加元素而改变长度
2.数组的方法:concat(),every(),forEach(),join().....
真数组可以使用数组中的方法
伪数组不可以使用数组中的方法
arguments对象不是一个 数组 。它除了length属性和索引元素之外没有任何Array属性。