在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用。
虽然for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时我们就可以使用JS中的函数。
函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。
函数在使用时分为两步:声明函数和调用函数
<script>
//1.声明函数
function sayHi(){
console.log('hello word');
}
//2.调用函数
sayHi(); //调用函数一定要加小括号
script>
在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参,而在调用该函数时同样也需要传递相应的参数,这些参数被称为实参。
参数 | 说明 |
---|---|
形参 | 形式上的参数 函数定义的时候 传递的参数 当前并不知道是什么 |
实参 | 实际上的参数 函数调用的时候 传递的参数实参 是传递给形参的 |
参数的作用:在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去
注意:在javaScript中,形参是默认值是undefined
<script>
function getSum(num1,num2){
console.log(num1+num2);
}
//1.如果实参传递的参数和形参一样 则正常输出结果
getSum(1,2); //3
//2.如果实参传递个数多于形参的个数 会取到形参的个数
getSum(1,2,3); //3
//3.如果实参的个数小于形参的个数
//形参可以看做是不用声明的变量 num2 是一个变量但是没有接受值 结果就是undefined
getSum(1); //NaN
script>
<script>
//2.return 只能返回一个值
function getResult(num1, num2) {
return num1, num2; //返回的结果是最后一个值
}
//3.return 后面的代码是不会执行的
function getResult(num1, num2) {
return [num1 + num2, num1 - num2, num1 * num2, num1 / num2,];
alert('我是不会执行的'); //alert 后面的代码是不会执行的
}
console.log(getResult(10, 20));
//4.我们的函数如果有return 则返回的是return 如果没有return 则返回undefined
function fun1() {
return 666;
}
console.log(fun1()); //函数返回的结果是 666
function fun2() { }
console.log(fun2()); //函数返回的结果是 undefined
script>
break:结束当前的循环体(如for、 while)
continue:跳出本次循环,继续执行下次循环(如for、while)
return:不仅可以退出循环,还能够返回 return语句中的值,同时还可以结束当前的函数体内的代码
当我们不确定有多少个参数传递的时候,可以用 arguments来获取。在 Javascript中, arguments实际上它是当前函数的一个内置对象。所有函数都内置了个 arguments对象, arguments对象中存储了传递的所有实参。
arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
<script>
// arguments的使用
function fn() {
var max=arguments[0];
console.log(arguments); //里面存储了所有传递过来的实参
for (var i = 0; i < arguments.length; i++) {
if(max<arguments[i]){
max=arguments[i];
}
console.log(arguments[i]);//1, 2, 3 ,1, 2, 3, 4, 5, 6
}
return max;
}
fn(1, 2, 3);
fn(1, 2, 3, 4, 5, 6);
console.log('最大值为:'+fn(1, 2, 3, 4, 5, 6));
console.log('最大值为:'+fn(1, 2, 3, 4, 5, 6,56,84,110,112,201,123,321,325,652,329,954));
script>
<script>
function isRunYear(year){
if((year%4==0 && year%100!=0)|| year%400==0){
// alert(year+'是闰年');
return true;
}else{
// alert(year+'不是闰年');
return false;
}
}
function twoMonth(){
// isRunYear();
var year = parseInt( prompt('请输入年份:'));
// var isisRunYear = isRunYear();
if(isRunYear(year)){
alert('当前年份是'+year +'年,是闰年'+',2月份有29天');
}else{
alert('当前年份是'+year +'是平年'+',2月份有28天');
}
}
twoMonth();
script>
<script>
// 函数2中声明方式
//1.利用函数关键字自定义函数(命名函数)
function fu(){
console.log('hello word');
}
fn();
//2.函数表达式(匿名函数)
// var 变量名 = function(){}
var fun = function(){
console.log('我是函数表达式');
}
fun();
//(1)fun是变量名 不是函数名
//函数表达式调用必须写在函数下面
//(2)函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数
script>
在 JavaScript中,根据作用域的不同,变量可以分为两种
全局变量
在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
局部变量
在局部作用域下声明的变量叫局部变量(在函数内部定义的变量)
全局变量和局部变量的区别
<script>
// 变量的作用域: 根据作用域的不同我们变量分为全局变量和局部变量
// 1. 全局变量: 在全局作用域下的变量 在全局下都可以使用
// 注意 如果在函数内部 没有声明直接赋值的变量也属于全局变量
var num = 10; // num就是一个全局变量
console.log(num);
function fn() {
var num1 = 15; // num1就是局部变量 只能在函数内部使用
num2 = 20; //局部变量没有加 var 也是全局变量
console.log(num);
}
fn();
script>
<script>
//作用域链:内部函数访问外部函数变量,采取的是链式查找的方式来决定取哪个值 这种结构我们称为作用域链 就近原则
// 简单来说就是就近原则
var num = 10;
function fn() { //外部函数
var num = 20;
function fun() { //内部函数
var num=35;
console.log(num); //35
}
fun()
}
var num=456;
fn();
script>
1、我们js引擎运行js分为两步:预解析 代码执行
2、预解析分为变量预解析(变量提升)和函数预解析(函数提升)
(1)变量提升就是把所有的变量声明提升到当前的作用域最前面 不提升赋值操作
(2) 函数提升 就是把所有的函数声明提升到当前作用域的最前面&ensp不调用函数
<script>
//1问
console.log(num); //报错 因为没有定义num变量
//2问(变量预解析)
console.log(num); //输出undefined 坑1
var num = 10;
//相等于输出了以下代码
var num;
console.log(num);
num=10;
//3问(函数预解析)
fn();
function fn() {
console.log('利用函数关键字定义函数');
}
//实际运行代码
function fn() {
console.log('利用函数关键字定义函数');
}
fn();
fun(); //报错 未定义 fum函数 坑2
var fun = function () {
console.log('利用函数表达式定义函数');
}
// 相等于执行了以下代码
var fun;
fun();
fun = function () {
// console.log('利用函数表达式定义函数');
}
script>
<script>
// 案例1
var num = 10;
fun();
function fun() {
console.log(num); //undefined
var num = 20;
}
//相当于执行了以下代码
// var num;
// function fun() {
// var num;
// console.log(num); //undefined
// num = 20;
// }
// num = 10;
// fun();
// 案例2
var num = 10;
function fn() {
console.log(num); //undefined
var num = 20;
console.log(num); //20
}
fn();
//相当于执行了以下代码
// var num;
// function fn(){
// var num;
// console.log(num);
// num=20;
// console.log(num);
// }
// num=10;
// fn();
//案例3
var a = 18;
f1();
function f1() {
var b = 30;
console.log(a); //undefined
console.log(b);//30
var a = '123';
}
//相当于执行了以下代码
// var a;
// function f1(){
// var b;
// var a;
// b=9;
// console.log(a); //undefined
// console.log(b);//9
// a='123';
// }
// a=18;
// f1();
//案例4
f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
var a = b = c = 9;
//相当于 var a=9,b=9,c=9;
console.log(a);
console.log(b);
console.log(c);
}
//相当于执行了以下代码
// function f1() {
// var a;
// //相当于 var a=9,b=9,c=9。B和C是直接赋值没有var 所以当全局变量看
// a=b=c=9;
// console.log(a);//9
// console.log(b);//9
// console.log(c);//9
// }
// f1();
// console.log(c);//9
// console.log(b);//9
// console.log(a);//报错 因为 a未定义
script>
现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象。
在 Javascript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的
保存一个值时,可以使用变量,保存多个值(一组值)时,可以使用数组。如果要保存一个人的完整信息呢
在 Javascript中,现阶段我们可以采用三种方式创建对象(object):
对象字面量:就是花括号{}里面包含了表达这个具体事物(对象)的属性和方法。
{ }里面采取键值对的形式表示
<script>
//利用对象字面量创建对象
// var obj = {}; //创建了一个空的对象
var obj = {
uname:'豆沙包',
age:18,
sex:'男',
sayHi:function(){
console.log('你好鸭');
}
}
//2、调用对象
//1 调用对象的属性 我们采取: 对象名.属性
console.log(obj.uname); //豆沙包
//2 调用对象的另一种方法 对象名['属性名']
console.log(obj['age']);//18
//3 调用对象的方法 sayHi 对象名.方法名() 千万别忘记添加小括号
obj.sayHi();
script>
<script>
// 利用 new object 创建对象
var obj = new Object();
obj.name = '鸣人';
obj.age = '18';
obj.sex = '男';
obj.address = '北京';
obj.skill = function () {
console.log('影分身');
}
// 我们利用 等号 = 赋值的方法 添加对象的属性和方法
// 每个属性和方法之间用 分号结束
console.log(obj.name);
console.log(obj['name']);
obj.skill();
script>
构造函数:是种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
1.在内存中创建一个新的空对象
2.让this指向这个新的对象
3.执行构造函数里面的代码,给这个新对象添加属性和方法。
4.返回这个新对象(所以构造函数里面不需要 return)
for…in语句用于对数组或者对象的属性进行循环操作。
<script>
var obj = {
name='豆沙包',
age=18,
sex='男',
address='北京',
fn:function(){}
}
// for..in遍历我们对象
// for(变量 in 对象){
// }
for(var k in obj){
console.log(k); //k 变量输出 得到的是属性名
console.log(obj[k]); //obj[k] 得到的是 属性值
}
// 我们使用for in 里面的变量 我们喜欢写 k 或者key
script>
MDN
Math对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用Mat中的成员。
Math.random()
<script>
//Math.random() 函数返回一个浮点数, 伪随机数在范围从0到小于1,也就是说,从0(包括0)往上,但是不包括1(排除1),然后您可以缩放到所需的范围。实现将初始种子选择到随机数生成算法;它不能被用户选择或重置。
console.log(Math.random());
//得到一个两数之间的随机整数,包括两个数在内
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(getRandom(1, 10));
//随机点名
var arr = ['豆沙包', '干饭王', '胡歌', '彭于晏', '刘亦菲'];
console.log(arr[getRandom(arr.length-1, 0)]);
script>
1、获取当前时间必须实例化
<script>
var date = new Date();//创建一个时间对象
console.log(date);
//2、参数常用的写法 数字型 2021,12,13 或者是 字符串型 '2021-12-13 19:30:20'
script>
2、Date()构造函数的参数
<script>
//2、参数常用的写法 数字型 2021,12,13 或者是 字符串型 '2021-12-13 19:30:20'
var data1 = new Date(2021,12,13);
var data2 = new Date('2021-12-13 19:30:20');
script>
我们想要2021-12-13 8:8:8格式的日期,要怎么办?
需要获取日期指定的部分,所以我们要手动的得到这种格式。
<script>
//格式化日期 年月日
var date = new Date();
var year = date.getFullYear();//返回当年日期的年 2021
var month = date.getMonth() + 1;//月份 返回的月份小了一个月 所以记得+1鸭
var dates = date.getDate();//返回的是几号
var day = date.getDay();//周一返回的是1 周六返回的是6 周日返回的是0
function getTime() {
var date = new Date();
var h = date.getHours(); //时
h = h < 10 ? '0' + h : h;
var m = date.getMinutes();//分
m = m < 10 ? '0' + m : m;
var s = date.getSeconds();//秒
s = s < 10 ? '0' + s : s;
return h + ':' + m + ':' + s;
}
var arr = [' 星期日 ', ' 星期一 ', ' 星期二 ', ' 星期三 ', ' 星期四 ', ' 星期五 ', ' 星期六 '];
console.log('今天是' + year + '年' + month + '月' + dates + '日 ' + arr[day] + getTime());
script>
Date对象是基于1970年1月1日(世界标准时间)起的亳秒数,我们经常利用总的毫秒数来计算时间,因为它更精确
<script>
//获得Date总的毫秒数(时间戳) 不是当前时间的毫秒数 而且距离1970年1月1号过了多少毫秒数
//1.通过 Valueof() getTime();
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
// 2.简单的写法(最常用的写法)
var date1 = +new Date(); //+new Date()返回的是总的毫秒数
console.log(date1);
//3.H5新增的 获得总的毫秒数
console.log( Date.now());
script>
<script>
<script>
//①核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能拿着时分秒相减,比如05分减去25分,结果会是负数的。**
//②用时间戳来做。用户输入时间总的亳秒数减去现在时间的总的亳秒数,得到的就是剩余时间的毫秒数。**
//获得总的毫秒数
function countDown(time) {
var nowTime = +new Date(); //()括号为空 返回的是当前时间总的毫秒数
var inputTime = +new Date(time); //返回的是用户输入的时间总的毫秒数
var times = (inputTime - nowTime) / 1000; //输入时间减去现在时间总的毫秒数 1秒等于1000豪秒
var d = parseInt((times / 60 / 60 / 24));//天
d = d < 10 ? '0' + d : d;
var h = parseInt((times / 60 / 60 % 24));//小时
h = h < 10 ? '0' + h : h;
var m = parseInt((times / 60 % 60));//分钟
m = m < 10 ? '0' + m : m;
var s = parseInt((times % 60));//秒
s = s < 10 ? '0' + s : s;
return d + '天' + h + '时' + m + '分' + s + '秒';
}
console.log(countDown('2021-12-14 14:00:00'));
var times = new Date();
console.log(times);
script>
script>
为了方便操作基本数据类型, Javascript还提供了三个特殊的引用类型: String、Number和 Boolean。基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。
按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为JS会把基本数据类型包装为复杂数据类型,其执行过程如下
指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。
字符串所有的方法,都不会修改字符串本身字符串是不可变的,操作完成会返回一个新的字符串。
<script>
//根据索引返回
var str = 'andy';
console.log(str.charAt(2));
for(var i =0;i<str.length;i++){
console.log(str.charAt(i));
}
// 2.charcodeAt(index) 返回相应索引号的字符的ASCII值 目的:判断用户按了哪个键
console.log(str.charCodeAt(0)); // 97
//3.H5新增的 str[index]
console.log(str[0]);
</script>
//字符串操作方法
//1.concat('字符串1','字符串2'...);
var str = 'andy';
console.log(str.concat('red'));//andyred
//2.substr('截取的起始位置,截取几个字符')
var str1 = '改革春风吹满地';
console.log(str1.substr(2, 2)); //春风
//3.替换字符 replace('被替换的字符','替换的字符');//他只会替换第一个字符
console.log(str.replace('a', 'b')); //bndy
var str2 = 'sdasofsofsodosodgrgdoadwafo';
while (str2.indexOf('o') !== -1) {
str2 = str2.replace('o', '*');
}
console.log(str2);//sdas*fs*fs*d*s*dgrgd*adwaf*
//4. 字符转换为数组 solit('分隔符') 前面我们学过 join 把数组转换为字符串
var str3 = 'red,pink,blue';
console.log(str3.split(','));// ['red', 'pink', 'blue']
console.log('doushabao'.toUpperCase());//调用该方法的字符串转为大写形式并返回 DOUSHABAO
console.log('RED'.toLowerCase()); //调用该方法的字符串转为小写形式并返回 red
简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。
堆栈空间分配区别:
<script>
// 复杂数据传参
function PerSon(name){
this.name = name;
}
function f1(x){
console.log(x.name); //2.这个输出什么? 刘德华
x.name='张学友';
console.log(x.name); //3.这个输出什么? 张学友
}
var p = new PerSon('刘德华');
console.log(p.name);//1. 这个输出什么? 刘德华
f1(p);
console.log(p.name);//4. 这个输出什么?张学友
</script>
面向过程就是分析出解决问题所需要的步骤,然后用函麴把这些步骤步步实现,使用的时候再一个一个的依次调用就可以了。
举个栗子:将大象装进冰箱,面向过程做法:
面向过程,就是按照我们分析好了的步骤,按照步骤解决问题。
面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作
举个栗子:将大象装进冰箱,面向对象做法。
先找出对象,并写出这些对象的功能:
1.大象对象
2.冰箱对象
3.使用大象和冰箱的功能
面向对象是以对象功能来划分问题,而不是步骤
在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。
面向对象编程具有灵活、代码可复用、容易维护秆开发的优点,更适合多人合作的大型软件项目。
面向对象的特性:
面向过程
用面向过程的方法写出来的程序是一份蛋炒饭,而用面向对象写出来的程序是一份盖浇饭。
现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个远程务器的连接也可以是“对象。
在 JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的
属性:事物的特征,在对象中用属性来表示(常用名词)
方法:事物的行为,在对象中用方法来表示(常用动词)
在ES6中新增加了类的概念,可以使用class关键字声明一个类,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某大类(class)
对象特指某一个,通过类实例化一个具体的对象
面向对象的思维特点:
1.抽取(抽象)对象共用的属性和行为组织封装)成一个类(模板)
2.对类进行实例化获取类的对象
语法:
class name{
//class body
}
创建实例:
var xx = new name();
注意:类必须使用new实例化对象
<script>
//1.创建类 class 创建一个明星类
class Star {
constructor(uname) {
this.uname = uname;
}
}
//2.利用类创建对象 new
var ldh = new Star('刘德华');
var zxy = new Star('张学友');
console.log(ldh.uname);
console.log(zxy.uname);
//(1)通过c1ass关键字创建类,类名我们还是习惯性定义首字母大写
// (2)类里面有个 constructor函数,可以接受传递过来的参数,同时返回实例对象
// (3) constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类
// 也会自动生成这个函数
// //(4)生成实例new不能省略
// //(5)最后注意语法规范,创建类类名后面不要加小括号,生成实例类名后面加小括号,构造函数不需要加 function
</script>
现实中的继承:子承父业,比如我们都继承了父亲的姓。
程序中的继承:子类可以继承父类的一些属性和方法。
语法:
class Father {}
class Son extends Father {}
super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。
class Father {
say() {
return '我是爸爸';
}
constructor() {
}
money() {
console.log('100块钱');
}
}
class Son extends Father {
say() {
console.log(super.say() + '的爷爷');
}
}
var son = new Son();
son.say();
注意:子类在构造函数中使用supr,必须放到this前面(必须先调用父类的构造方法,在使用子类构造方法)
三个注意点:
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与ew一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
new在执行时会做四件事情:
我们希望所有的对象使用同一个函数,这样就比较节省内存,那么我们要怎样做呢?
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype.对象上,这样所有对象的实例就河以共享这些方法。
问答?
1.原型是什么?
一个对象,我们也称为prototype为原型对象。
2.原型的作用是什么?
共享方法。
对象都会有一个属性proto指向构造函数的prototype原型对象,之所以我们对像可以使用构造函数
prototype,原型对象的属性和方法,就是因为对象有_proto_原型的存在。
_proto_对象原型和原型对像prototype是等价的
_proto_对象原型的意义就在于为对像的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
可以通过原型对象,对原来的内置对像进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能。
注意:数组和字符串内置对蟓不能给原型对象覆盖操作Array.prototype=0,只能是Array.prototype.x=function0的方式。
<script>
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());
</script>
ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:
迭代(遍历)方法:forEach()、map()、filter()、some0、every0;
array.forEach(function(currentvalue,index,arr))
<script>
//forEach迭代(遍历)数组
var arr = [1,2,3];
arr.forEach(function(value,index,array){
console.log('每个数组元素'+value);
console.log('每个数组元素的索引号'+index);
console.log('数组本身'+array);
})
</script>
array.filter(function(currentvalue,index,arr))
<script>
var arr = [1, 2, 3];
var newArr = arr.filter(function (value, index) {
return value > 20;
})
</script>
array.some (function(currentvalue,index,arr))
<script>
var arr = [1, 2, 3,56,32,48,69,63,21,52];
var flag = arr.filter(function (value, index) {
return value > 20;
})
console.log(flag); //true
</script>
<script>
var str = ' 豆沙包 ';
console.log(str);
var str1 = str.trim();
console.log(str1);
</script>
object.keys(obj)
<script>
// 用于获取对象自身所有的属性
var obj = {
id: 1,
name: '豆沙包',
age: 18,
addr: '北京'
}
var arr = Object.keys(obj);
console.log(arr); //['id', 'name', 'age', 'addr']
arr.forEach(function (value) {
console.log(value);
})
</script>
object.defineProperty(obj,prop,descriptor)
Object.definePrpperty0第三个参数descriptori说明:以对象形式{}书写
value::设置属性的值默认为undefined
writable:值是否可以重写。true|false默认为false
enumerable::目标属性是否可以被枚举。true|false默认为false
configurable:目标属性是否可以被删除或是否可以再次修改特性true|false默认为false
1.函数声明方式function关键字(命名函数)
2.函数表达式(匿名函数)
3.new Function()
var fn=new Function(‘参数1‘,‘参数2‘.,‘函数体‘)
<script>
// 函数的定义方式
// 1. 自定义函数(命名函数)
function fn() {};
// 2. 函数表达式 (匿名函数)
var fun = function() {};
// 3. 利用 new Function('参数1','参数2', '函数体');
var f = new Function('a', 'b', 'console.log(a + b)');
f(1, 2);
// 4. 所有函数都是 Function 的实例(对象)
console.dir(f);
// 5. 函数也属于对象
console.log(f instanceof Object);
</script>
这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同一般指向我们的调用者.
JavaScript为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题,常用的有bind()、call()、apply()三种方法。
<script>
// 1. call()
var o = {
name: 'andy'
}
function fn(a, b) {
console.log(this); console.log(a + b);
};
fn.call(o, 1, 2);
// call 第一个可以调用函数 第二个可以改变函数内的this 指向
// call 的主要作用可以实现继承
function Father(uname, age, sex) {
this.uname = uname;
this.age = age;
this.sex = sex;
}
function Son(uname, age, sex) {
Father.call(this, uname, age, sex);
}
var son = new Son('刘德华', 18, '男');
console.log(son);
</script>
2.apply方法
apply()方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。
<script>
// 1. 也是调用函数 第二个可以改变函数内部的this指向
// 2. 但是他的参数必须是数组(伪数组)
// 3. apply 的主要应用 比如说我们可以利用 apply 借助于数学内置对象求数组最大值
// Math.max();
var arr = [1, 66, 3, 99, 4];
var arr1 = ['red', 'pink'];
var max = Math.max.apply(Math, arr);
var min = Math.min.apply(Math, arr);
console.log(max, min);
</script>
fun.apply(thisArg,【argsArray】)
相同点:
都可以改变函数内部的this指向
区别点:
主要应用场景:
<script>
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
this.disabled = true;
setTimeout(function() {
this.disabled = false;
}.bind(this), 2000);
}
}
</script>
JavaScript除了提供正常模式外,还提供了严格模式(strict mode。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。
严格模式在E10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的avaScript语义做了一些更改:
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
1.为脚本开启严格模式
有的script基本是严格模式,有的script脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他script脚本文件。
<script>
<!-- 为某个函数开启严格模式 -->
<script>
// 此时只是给fn函数开启严格模式
function fn() {
'use strict';
// 下面的代码按照严格模式执行
}
function fun() {
// 里面的还是按照普通模式执行
}
</script>
严格模式对Javascript的语法和行为,都做了一些改变。
1、变量规定
2、严格模式下this指向问题
3、函数变化
①函数不能有重名的参数。
②函数必须声明在顶层新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。
更多严格模式要求参考:https:/developer.mozilla.org/hCN/docs/Web/JavaScript/Reference/Strict mode
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
此时fn就是一个高阶函数函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
闭包(closure)指有权访间另一个函数作用域中变量的函数。–JavaScript高级程序设计简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。
闭包的主要作用: 延伸了变量的作用范围
<script>
// 闭包(closure)指有权访问另一个函数作用域中变量的函数。
// 一个作用域可以访问另外一个函数的局部变量
// 我们fn 外面的作用域可以访问fn 内部的局部变量
// 闭包的主要作用: 延伸了变量的作用范围
function fn() {
var num = 10;
// function fun() {
// console.log(num);
// }
// return fun;
return function() {
console.log(num);
}
}
var f = fn();
f();
</script>
<script>
<ul class="nav">
<li>榴莲</li> <li>臭豆腐</li><li>鲱鱼罐头</li><li>大猪蹄子</li>
</ul>
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i)
}
</script>
1.闭包是什么?
2.闭包的作用是什么?
如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。
简单理解:函数内部自己调用自己这个函数就是递归函数
递归函数的作用和循环效果一样
由于递归很容易发生“栈谥出”错误(stack overflow),所以必须要加退出条件return。
<script>
// 利用递归函数求1~n的阶乘 1*2*3....*..n
function fn(n) {
if (n == 1) {
return n;
}
return n * fn(n - 1);
}
console.log(fn(4));
//详细思路 加入用户输入的是3
// return 3 *fn(2)
// return 3 *(2 * fn(1))
// return 3 *(2 * 1)
// return 3 * 2
// return 6
</script>
<script>
// 利用递归函数求斐波那契数列(兔子序列)1、1、2、3、5、8、13、21.
// 用户输入一个数字就可以求出这个数字对应的兔子序列值
// 我们只需要知道用户输入的n的前面两项(n-1n-2)就可以计算出n对应的序列值
function fbnq(n) {
if (n == 1 || n == 2) {
return 1;
}
return fbnq(n - 1) + fbnq(n - 2);
}
console.log(fbnq(4));
</script>
<script>
function fn(json, id) {
var o = {};
json.forEach(function (item) {
if (item.id == id) {
// console.log(item);
o = item;
return item;
} else if (item.goods && item.goods.length > 0) {
o = fn(item.goods, id)
}
})
return o;
}
</script>
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在avaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规侧)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线,昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
其他语言也会使用正则表达式,本阶段我们主要是利用avaScript正则表达式完成表单验证。
**在JavaScript中,可以通过两种方式创建一个正则表达式。**
1.通过调用RegExp对象的构造函数创建
2.通过字面量创建
test()正则对象方法,用于检测字符串是否符合该规则,对象会返回true或false,其参数是测试字符串。
regexobj.test(str)
<script>
// 正则表达式在JS中的使用
//1. 利用RegExp对象来创建 正则表达式
var regexp = new RegExp(/123/);
console.log(regexp);
//2.利用字面量创建 正则表达式
var rg = /123/;
//3.test 方法用来检测字符串是否符合正则表达式要求的规范
console.log(rg.test(123));
console.log(rg.test('abc'));
</script>
一个正则表达式可以由简单的字符构成,比如/abc/,也可以是简单和特殊字符的组合,比如/ab*c/。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如八、$、+等。
特殊字符非常多,可以参考:
MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular Expressions
jQuery手册:正则表达式部分
正则测试江具:http:/tool.oschina.net/regex
这里我们把元字符划分几类学习。
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符。
如果^和$在一起,表示必须是精确匹配。
<script>
//{3,} 大于等于三
var rg = /^a{3,}$/;
console.log(rg.test('aaa')); //true
console.log(rg.test('aaaa'));// true
console.log(rg.test('aa'));//false
</script>
可以在线测试:https://c.runoob.com/front-end/854/
<script>
// 中括号 字符集合.匹配方括号中的任意字符.
// var reg = /^[abc]$/;
// a 也可以 b 也可以 c 可以 a ||b || c
// 大括号 量词符. 里面表示重复次数
// var reg = /^abc{3}$/; // 它只是让c重复三次 abccc
// console.log(reg.test('abc'));
// console.log(reg.test('abcabcabc'));
// console.log(reg.test('abccc'));
// 小括号 表示优先级
var reg = /^(abc){3}$/; // 它是让abc重复三次
console.log(reg.test('abc'));
console.log(reg.test('abcabcabc'));
console.log(reg.test('abccc'));
</script>
<script>
// 座机号码验证: 全国座机号码 两种格式: 010-12345678 或者 0530-1234567
// 正则里面的或者 符号 |
// var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/;
var reg = /^\d{3,4}-\d{7,8}$/;
</script>
replace()方法可以实现替换字符操作,用来替换的参数可以是一个字符串或是一个正则表达式。
stringObject.replace(regexp/substr,replacement)
/表达式/[switch] //switch 可以写成 g或者i或者gi
switch(也称为修饰符)按照什么样的模式来匹配.有三种值:
g:全局匹配
ⅰ:忽略大小写
gi:全局匹配+忽略大小写
<script>
div.innerHTML = comment.value.replace(/激情|gay/g, '***');
</script>