JS基础语法(二)

JavaScript基础语法(二)

八、 函数

1、 函数的概念

在JS里面,可能会定义非常多的相同代码或者功能相似的代码,这些代码可能需要大量重复使用虽然,for循环语句也能实现一些简单的重复操作,但是比较具有局限性,此时,我们就可以使用JS中的函数

函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用

2、 函数的使用

声明函数

function 函数名() {
    // 函数集
}
function sayHi() {
    console.log("hi~~")
}

function 声明函数的关键字,全部小写

函数是做某件事情,函数名一般是动词

函数不调用自己不执行

调用函数

函数名()  // 调用函数
sayHi()

调用函数的时候一定不要忘记添加小括号

声明函数本身并不会执行代码,只有调用函数时才会执行函数体中的代码

3、 函数的封装

函数的封装是把一个或多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口

function getSum() {
    var sum = 0;
    for (var i = 1; i <= 100; i++){
        sum += i;
    }
    console.log("和为:" + sum);
}

getSum();

4、 函数的参数

形参:形式上的参数,函数定义的时候传递的参数

实参:实际上的参数,函数调用的时候传递的参数,实参是传递给形参的

实现了函数重复不同的代码

function 函数名(形参1, 形参2···) {  // 在声明函数的小括号里面是形参
   
}

函数名(实参1, 实参2···)  // 在函数调用的小括号里面的是实参

形参和实参的执行过程

  • 形参是接收实参的变量

    function eat(arg) {
        console.log(arg);
    }
    eat("土豆");
    eat("肉");
    
  • 函数的参数可以有,也可以没有

  • 注意点

    • 多个参数之间用逗号隔开
    • 形参可以看做是不用声明的变量

函数的参数匹配问题

如果实参的个数和实参的个数一致,则正常输出结果

如果实参的个数多于形参的个数,会取到形参的个数

如果实参的个数少于形参的个数,没有接收值的形参其是undefined

建议

  • 尽量实参的个数和形参的个数相匹配

5、 函数返回值

return语句

  • 有的时候,我们会希望函数将返回值给调用者,此时通过使用return语句可以实现
function 函数名() {
    
    return 返回值;
}

接收变量 = 函数名();
  1. 我们函数只是实现某种功能,最终结果需要返回给函数的调用者。通过return实现

  2. 只要函数遇到return就把后面的结果返回给函数的调用者

    function getReturn() {
        return 10;
    }
    var num = getReturn();  // 接收返回值
    console.log(num)
    

注意事项

  • return 终止函数

  • return 只能返回一个值,如果用逗号隔开多个值,返回结果是最后一个值

    function getResult(num1, num2) {
        return [num1+num2, num1-num2, num1*num2, num1/num2]
    }  // 使用数组来保存多个返回值
    
  • 函数没有 return 则返回 undefined

    function fuc1() {
        var a = 1;
    }
    
    function fuc2() {
        return 2;
    }
    
    console.log(fuc1());  // 没有return返回undefined
    console.log(fuc2());
    
  • return 也可以用于终止循环

6、 arguments

当我们不确定有多少个参数传递的时候,可以用arguments来获取。在JS中,argument 实际上,它是属于当前函数的一个内置对象。所有函数都内置了一个arguments对象,argument对象中存储了传递的所有实参

function fun() {
    console.log(arguments);  // 里面存储了传递过来的所有的实参对象
	console.log(arguments.length);  // 具有数组的length属性
}
fun(1, 21, 32);
  • arguments:伪数组,并不是真正意义上的数组
    1. 具有数组的length属性
    2. 按照索引的方式进行存储的
    3. 它没有真正数组的一些方法 pop() push() 等
  • 只有函数才有 arguments 对象,而且是每个函数都内置好了这个 arguments
// 求任意个数的最大值
function getMax() {
    var max = 0;
    for (var i = 1; i < arguments.length; i++) {
        if (max < arguments[i]) {
            max = arguments[i]; 
        }
    }
    console.log(max);
}

getMax(1, 2, 3, 8, 0, 6, 89)

7、 函数调用函数

因为每个函数都是独立的代码块,用于完成特殊任务,因此经常会用到函数相互调用的情况

function fn1() {
    console.log(11);
    fn2();  // 在fn1调用了fn2函数
}

function fn2() {
    console.log(34);
    fn1();
}

fn1();

8、 函数的声明方式

  • 利用关键字自定义函数

    function fu1() {}
    
  • 利用函数表达式(匿名函数)

    var 变量名 = function() {}  // 调用方式一样
    

    函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值,而表达式里面存的是函数

九、 作用域

1、 概述

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突

2、 全局作用域

整个Script标签,或者是单独的JS文件,即在任意位置都可以使用

注意

  • 如果在函数内部没有声明,直接赋值的变量也是全局变量
  • 全局变量只有浏览器关闭的时候才会销毁,比较占内存

3、 局部作用域

在函数内部就是局部作用域,这个代码的名字只在函数内部其效果和作用

注意

  • 函数的形参也可以看做局部变量
  • 局部变量,当我们程序执行完毕之后就会销毁,比较节约资源

现阶段JS没有块级作用域,在es6的时候新增了块级作用域

4、 作用域链

只要是代码,就至少有作用域

写在函数内的局部作用域,如果在函数内还有局部作用域,那么在这个作用域中就又可以诞生一个作用域

根据内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称为作用域链(就近原则)

5、 预解析

JS代码是由浏览器中的JS解释器来执行的。JS解析器在运行JS代码的时候分为两步:预解析和代码执行

  • 预解析:JS引擎会把JS里面所有的 var 还有 function 提升到当前作用域的最前面
    • 变量预解析(变量提升)
      • 变量提升,就是把所有的变量声明提升到当前作用域最前面,不提升赋值操作
    • 函数提升
      • 就是把所有的函数声明提升到当前作用域的最前面,不调用函数
  • 代码执行:按照代码书写的顺序从上往下执行

十、 自定义对象

1、 概念

在JS中,对象是一组无序的相关属性和方法的集合,所有的万物都是对象,例如:字符串、数值、数组、函数等

对象是由属性和方法组成

  • 属性:事物的特征。在对象中用属性表示(名词)
  • 方法:事物的行为,在对象中用方法表示(动词)

保存一个值时,可以使用变量,保存多个值(一组值)时,可以使用数组。如果要完整值呢?

JS中的对象表达结构更清晰,更强大

2、 创建对象

变量属性函数方法的区别

  • 相同点
    • 都是存储数据的
  • 变量,单独声明并赋值,使用的时候直接写变量名,单独存在
  • 属性,在对象里面的不需要声明的,使用的时候必须是,对象.属性
  • 函数和方法的相同点
    • 都是实现某种功能,做某种事
  • 函数和方法的不同点
    • 函数是单独声明的,并且调用的时候 函数名() 单独存在的
    • 方法,在对象里面,调用的时候 对象名.方法名()

在JS中现在主要采用三种方式创建对象

  • 利用字面量创建对象

    • 对象字面量:就是花括号 { } 里面包含了表达这个具体事物(对象)的属性和方法

      var obj = {
          name: "zhansan",
          age: 2,
          sex: "男",
          sayHi: function() {
              console.log("hi~~");
          }
      };  // 创建了一个空对象
      
      // 调用对象,我们采取,对象名.属性名
      console.log(obj.name);
      
      // 第二种方法,对象名["属性名"]
      consloe.log(obj["name"]);
      
      // 调用对象的方法  对象名.方法名()
      obj.sayHi()
      
      1. 里面的属性或者方法我们采用键值对的形式
      2. 多个属性或者方法中间用逗号隔开的
      3. 方法冒号后面跟的是一个匿名函数
  • 利用 new Object 创建对象

    var obj = new Object();  // 创建了一个空对象
    obj.nam = "zhannsan";
    obj.age = 18;
    obj.sex = "女";
    obj.sayHi = function() {
        console.log("hi~~");
    }
    
    • 我们利用等号赋值的方法,添加对象的属性和方法
    • 每个属性和方法之间用分号结束
    • 调用属性的方法相同
  • 利用构造函数创建对象

    前面两种创建对象的方式一次只能创建一个对象

    我们一次创建一个对象,里面很多的属性和方法是大量相同的,我们只能复制

    因此我们可以利用函数的方法,重复这些相同的代码,我们就把这个函数称为构造函数;又因为这个构造函数不一样,里面封装的不是普通代码,而是对象——构造函数抽象出来就是把我们对象里面一些相同的属性和方法抽象出来,封装到函数里面

    // 我们需要创建四大天王的对象 相同的属性:名字、年龄、性别 相同的方法:唱歌
    /*
    function 构造函数名() {
        this.属性 = 值;
        this.方法 =function() {}
    }
    new 构造函数名();  // 调用函数
    */
    
    function Star(uname, age, sex) {
        this.name = uname;
        this.age = age;
        this.sex = sex;
        this.sing = function(song) {
            console.log(song);
        }
    }
    var ldh = new Star("刘德华", 18, "男", "冰雨"); 
    console.log(ldh);  // 调用返回的对象是一个对象
    

    构造函数名字首字母要大写

    我们构造函数不需要啊写 return 就可以返回结果

    我们要构造函数,必须使用 new

    我们只要 new Star() 调用函数就创建一个对象

    我们的属性和方法前面必须加上this

    • 构造函数和对象的联系
      • 构造函数泛指某一大类,它类似于Java语言里面的类
      • 对象特指是一个具体事物
      • 我们构造函数创建对象的过程也叫做对象的实例化
    • new关键字执行过程
      • new 构造函数可以在内存中创建一个空对象
      • this 就会指向刚才创建的空对象
      • 执行函数里面的代码,给这个空对象添加属性和方法
      • 返回这个对象

3、 遍历对象

for ... in 语句用于对数组或者对象的属性进行循环操作

var obj = {
    name: "zhansan",
    age: 2,
    sex: "男",
    sayHi: function() {
        console.log("hi~~");
    }
};  

// 语法:for (变量 in 对象) {}
for (var k in obj) {
    console.log(k);  // 得到的是属性名
    console.log(obj[k]);  // 得到变量
}

十一、 内置对象

1、 概念

JS中的对象分为三种:自定义对象、内置对象、浏览器对象

内置对象就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或者是最基本而必要的功能(属性和方法)

JS提供了多个内置对象:Math、Date、 Array、 String等

2、 查文档

学习一个内置对象的使用,只要学会其常用成员的使用即可,我们可以通过查文档学习,可以通过MDN / W3C来查询

3、 Math对象

其不是一个构造函数,所以我们不需要使用new方法,直接使用即可

console.log(Math.PI);  // 圆周率
console.log(Math.max(1, 99, 3));  // 求最大值,如果没有参数返回-Infinitfty 
console.log(Math.min(1, 99, 3)); // 求最小值
console.log(Math.abs(-1));  // 绝对值,存在隐式转换
console.log(Math.floor());  // 向下取整
Math.ceil()  // 向上取整
Math.round()  // 四舍五入,就近取整,注意-3.5=-3, 3.5 = 4

封装自己的数学对象

var myMath = {
    PI: 3.14155926,
    max: function() {
        var max = arguments[0];
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] > max) {
                max = arguments[i];
            }
        }
        return max;
    },
    min: function() {
        var min = arguments[0];
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] < min) {
                min = arguments[i];
            }
        }
        return min;
    }
}

console.log(myMath.PI);
console.log(myMath.max(1, 5, 9));
console.log(myMath.min(1, 5, 9));

随机数方法

Math.random()返回一个随机的小数(0 <= x < 1)

console.log(Math.random());  // 不需要参数

我们想要得到两个数之间的随机整数,并且包含这两个整数

Math.floor(Math.random() * (max - min + 1)) + min;

function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(getRandom(1, 10));  
猜数字游戏
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}  // 生成1到10的随机数
var rand_num =  getRandom(1, 10);
while (true) {
	var num = parseInt(prompt("请输入要猜的数字(1 <= x <= 10)"));
    if (num === rand_num) {
        alert("好帅哦,猜对了!");
        break;
    } else if (num > rand_num) {
        alert("抱歉,猜大了哦!加油");
    } else {
        alert("抱歉,猜小了哦!亲");
    }
}

4、 Date对象

其为构造函数,必须使用new来调用创建我们的日期对象

var date = new Date();
console.log(date);  // 如果没有参数返回当前时间
// 参数常用写法,数字型、字符串型
var date1 = new Date(2022, 1, 25);
console.log(date1);  // 返回2022.2,数字类型

var date2 = new Date("2022-1-25 8:8:8");
console.log(date2);  // 返回2022-1,字符串类型

Date对象和Math对象不一样,它是一个构造函数,所以我们需要实例化后才能使用

Date实例用来处理日期和时间

日期格式化

var date = new Date(); 
console.log(date.getFullYear());  //返回当前日期的年
console.log(date.getMonth() + 1);  // 获取当前的月份,小一个月
console.log(date.getDate());  // 返回日期
console.log(date.getDay());  // 周日返回的是0,其他的正常

var year = date.getFullYear();
var month = date.getMonth() + 1;
var date = date.getDate();
var day = date.getDay();
var hour = date.getHours();
var mit = date.getMinutes();
var sec = date.getSeconds();
console.log("今天是:" + year + "年" + month + "月" + date + "日" + "星期" + day)
// 要求封装一个函数,返回当前的时间
function getTime() {
    var time = new Date();
    var h = time.getHours();
    h = h < 10 ? "0" + h : h;
    var m = time.getMinutes();
    m = m < 10 ? "0" + m : m;
    var s = time.getSeconds();
    s = s < 10 ? "0" + s : s;
    return h + ":" + m + ":" + s; 
}

console.log(getTime());  //格式化输出

获取时期的总的毫秒数

1970年1月1日开始,获取毫秒数,也叫时间戳

// 通过valueOf()得到,或者getTime()
var date = new Date();
console.log(date.valueOf());  // 距离1970年1月1日的毫秒数
console.log(date.getTime());

// 简单的写法
var date1 = +new Date();
console.log(date1);

// H5中的方法
console.log(Date.now());

倒计时案例

核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能拿着时分秒相减

用时间戳来做,用户输入时间总的毫秒数减去现在的时间的总毫秒数,得到的就是剩余时间的毫秒数

把剩余的时间的毫秒数转换天、时、分、秒(时间戳转换为时分秒)

function countDown(time) {
    var nowTime = +new Date();  // 获取当前时间戳
    var inputTime = +new Date(time);  // 返回的是用户输入时间总的时间戳
    var times = (inputTime - nowTime) / 1000;  // time是剩余时间总的毫秒数
    var d = parseInt(times / 60 / 60 / 24 );
    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("2022-2-1 24:00:00"));

5、 Array对象

创建数组

  • 利用数组字面量

    var arr = [1, 2, 3];
    
  • 利用Array对象

    var arr1 = new Array(2);  // 创建了一个空的数组,长度为2
    console.log(arr1);
    
    var arr2 = new Array(2, 3);  // 等价于var arr2 = [2, 3]
    console.log(arr2);
    

检测是否为数组

  1. instanceof 运算符,它可以用来检测是否为数组

    var arr = [];
    var obj = {};
    console.log(arr instanceof Array);  // true
    console.log(obj instanceof Array);  // false
    
  2. Array.isArray(参数);

    console.log(Array.isArray(arr));  // true
    

添加和删除

方法名 说明 返回值
push(参数) 末尾添加一个或多个元素,注意修改原数组 返回新的长度
pop() 删除数组的最后一个元素,把数组长度减一,无参数 返回它删除元素的值
unshift(参数1···) 向数组的开头添加一个或更多元素,注意修改原数组 返回数组的新长度
shift() 删除数组的第一个元素,数组长度减一,无参数 返回第一个元素的值
var arr = [];
console.log(arr.push(4, "pink"));
console.log(arr);  // 有返回值,同时原数组也会发生变化

console.log(arr.unshift("red", 2, 10));  
console.log(arr);  // 有返回值,同时原数组也会发生变化

console.log(arr.pop());  // 返回删除的元素
console.log(arr.shift());  // 返回删除的元素

排序

方法名 说明 是否修改原数组
reverse() 颠倒数组中的元素 该方法会改变原来的数组,返回新数组
sort() 对数组元素进行排序 该方法会改变原来的数组,返回新数组
var arr = [1, 2, 3];

arr.reverse();
console.log(arr);

arr.sort(function(a, b) {
    return a - b;  // 升序排列
    // return b - a; // 降序排列
});
console.log(arr);

索引

方法名 说明 返回值
indexOf("要查找的字符", [ 起始的位置]) 数组中查找给定元素的第一个索引 如果存在返回索引号;反之,返回-1
lastIndexOf() 在数组中的最后一个的索引 如果存在返回索引号;反之,返回-1
数组去重
  1. 目标:把旧函数里面不重复的元素选取出来放到新数组中,重复的元素只保留一个,放到新数组中去
  2. 核心算法:遍历旧数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组里面没有出现过,我们就添加,否则不添加
var oldArr = ["c", "a", 'z', 'a', 'x', 'c', 'b'];

function unique(arr) {
	var newArr = [];
	for (var i = 0; i < oldArr.length; i++) {
    	if (newArr.indexOf(oldArr[i]) === -1) {  // 没有元素返回-1
        	newArr[newArr.length] = oldArr[i];
    	}
	}
    return newArr;
}

console.log(unique(oldArr));

数组转换成字符串

方法 说明 返回值
toString() 把数组转换成字符串,逗号分隔每一项 返回一个字符串
join("分隔符") 方法用于把数组中的所有元素转换为一个字符串 返回一个字符串
var arr = [1, 2, 3];

console.log(arr.toString());
console.log(arr.join("&"));

6、 String对象

基本包装类型

就是把简单的数据类型包装成复杂的数据类型

var str = "andy";
var temp = new String("any");
str = temp;  
temp = null;  // 销毁temp变量

字符串不可变

指的是里面的值不可变,虽然看上去可以改变内容,但是其实是内存地址改变了,内存中开辟了一个新的内存空间

索引

字符串所有的方法,都不会修改字符串本身(字符串是不可变的),操作完成会返回一个新的字符串

var str = "hello";
console.log(str.indexOf("h", 1));  // 从索引号为1的位置开始查找


// 查找字符出现的位置
var str = "abcoefoxyozzopp";
var index = str.indexOf("o");
var num = 0;
while (index !== -1) {
    console.log(index);
    num ++;
    index = str.indexOf("o", index + 1);
}
console.log("出现次数为:" + num);

根据位置返回字符

  1. charAt(index) 根据位置返回字符

    var str = "hello";
    console.log(str.charAt(3));
    
  2. charCodeAt(index)获取指定位置处的ASCII码值,判断用户按下的哪个键

    console.log(charCodeAt(0));
    
  3. str[index]获取指定位处出的字符

    console.log(str(3));
    
案例

判断一个字符串中出现次数最多的字符,并统计其次数

  • 利用 charAt() 遍历这个字符串
  • 把每个字符都存储给对象,如果对象没有该属性,就为1,存在就 +1
  • 遍历对象,得到最大值和该字符
var str = "abcoefoxyozzopp";
var obj = {};
for (var i = 0; i < str.length; i++) {
    var chars = str.charAt(i);  //chars 是字符串的每一个字符
    if (o[chars])  {
        o[chars]++;  // o[chars]得到的是属性值
    } else {
        o[chars] = 1;
    }
}
console.log(o);
// 遍历对象
var max = 0;
var char1 = "";
for (var k in o) {
    // k 得到的是属性名
    // o[k] 得到的是属性值
    if (o[k] > max) {
        max = o[k];
        char1 = k;
    }
}
console.log("最多的字符是:" + char1 + ",有:" + max + "个");

操作方法

方法名 说明
concat(str1...) concat()方法用于连接两个或多个字符串。拼接字符串,等效于+,+更常用
substr(start , length) 从start位置开始(索引号),length取的个数
slice(start, end) 从start开始,截取到end位置,end取不到
substring(startm, end) 从start位置开始,截取到end位置,end去不到,基本和slice相同,但是不会接受负值
var str = "rub";
console.log(str.concat("red"));

console.log(str.substr(0, 1));

替换字符

replace("被替换的字符", "替换的字符")

var str = "andy";
console.log(str.replace("a", "b"));  // 如果有多个,只替换第一个字符

字符串转换为数组

split("分隔符")

var str2 = "red, pink , blue";
console.log(str2.split(","));

7、 总结

简单数据类型和复杂数据类型

简单数据类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型

  • 值类型:简单数据类型/基本数据类型,在存储是变量中存储的是指本身,因此叫做值类型

    • 简单数据类型 null ,其为空对象
    • 如果有个变量我们以后打算存储为对象,暂时没有想好放啥,这个时候就给 null
  • 引用类型:复杂数据类型,在存储变量中存储的仅仅是地址(引用),因此叫做引用数据类型

    通过new关键字创建的对象(系统对象、自定义对象)

堆和栈

堆栈空间分配区别:

  1. 栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈

    简单数据类型存放到栈里面

  2. 堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,有垃圾回收机制回收

    复杂数据类型存放到堆里面

内存分配

  • 简单数据类型存放到栈里面,里面直接开辟一个空间,存放值
  • 复杂数据类型存放到堆里面,首先在栈里面存放地址,这个地址指向堆里面的数据

传参

简单数据类型

函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传递给函数的形参是,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到外部变量

复杂数据类型

函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象

posted @ 2022-01-25 08:31  Kenny_LZK  阅读(98)  评论(0编辑  收藏  举报