JavaScript基础(4)

正文:

  • JavaScript函数
  • JavaScript作用域
  • JavaScript预解析
  • JavaScript对象
  • JavaScript内置对象
  • JavaScript简单数据类型与复杂数据类型

 JavaScript函数

  • 函数的概念
  • 函数的使用
  • 函数的参数
  • 函数的返回值
  • arguments的使用
  • 函数案例
  • 函数的两种声明方式

函数的概念

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

函数的使用

函数在使用时分为两步:声明函数和调用函数

1)声明函数

// 声明函数
function 函数名() {
    // 函数体代码
}

function 是声明函数的关键字,必须小写

由于函数一般是为了实现某个功能才定义的,所有我们通常将函数名命名为动词,比如:getSum

2)调用函数

// 调用函数
函数名(); //通过函数名来执行函数体代码
  • 调用时千万不要忘记添加小括号

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

3)函数的封装

  • 函数的封装是把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口
  • 简单理解:封装类似于将电脑配件整合组装到机箱中(类似快递打包)

案例:利用函数计算1-100之间的累加和

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

函数的参数

形参和实参

在声明函数时,可以在函数名称后面的小括号中添加一些参数,这些参数被称为形参同样也需要传递相应的参数,这些参数被称为实参,而在调用该函数时

参数的作用:在函数内部某些值不能固定,我们可以通过参数在调用函数时传递不同的值进去
案例:利用函数求任意两个数的和

function getSum(num1, num2) {
    console.log(num1 + num2);
}
getSum(1,2);

案例:利用函数求任意两个数之间的和

function getSum(start, end) {
    var sum = 0;
    for (var i = start; i <= end; i++) {
        sum += i;
    }
    console.log(sum);
}
getSum(1, 100);
getSum(1, 10);

函数形参和实参个数不匹配问题

 小结:

  • 函数可以带参数也可以不带参数
  • 声明参数的时候,函数名括号里面的是形参,形参默认值为undefined
  • 调用函数的时候,函数名括号里面的是实参
  • 多个参数中间用逗号隔开
  • 形参的个数可以和实参的个数不匹配,但结果不可预计,我们要尽量匹配

 函数的返回值

return语句

有的时候,我们会希望函数将值返回给调用者,此时通过使用return语句就可以实现。

语法格式:

function 函数名() {
    return  需要返回的结果;
}
  • 我们函数只是实现某种功能,最终的结果需要返回给函数的调用者 函数名() 通过return实现的
  • 只要函数遇到return  就把后面的结果 返回给函数的调用者 函数名() = return后面的结果

案例:利用函数求任意两个数的最大值

function getMax(num1, num2) {
    if (num1 > num2) {
        return num1;
    } else {
        return num2;
    }
}
console.log(getMax(1, 3));

方法二

function getMax(num1, num2) {
    return num1 > num2 ? num1 : num2;
}
console.log(getMax(1, 3));

案例:利用函数求任意一个数组中的最大值

function getArrMax(arr) {
    var max = arr[0];
    for (var i = 1; i <= arr.length; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}
var re = getArrMax([5, 2, 99, 101, 67, 77]);
console.log(re);

 函数没有return返回undefined

函数都是有返回值的

  1. 如果有return则返回return后面的值
  2. 如果没有return则返回undefined

break、continue、return的区别

  • break:结束当前的循环体(如for、while)
  • continue:跳出本次循环,继续执行下次循环(如for、while)
  • return:不仅可以退出循环,还能够返回return语句中的值,同时还可以结束当前的函数体内的代码

arguments的使用

当我们不确定有多少个参数传递的时候,可以用arguments来获取。在JavaScript中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参。只有函数才有arguments对象,而且是每个函数都内置好了这个arguments。

arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:

  • 具有length属性
  • 按索引方式存储数据
  • 不具有数组的push,pop等方法
function fn() {
    for (var i = 0; i < arguments.length; i++) {
        console.log(arguments[i]);
    }
}
fn(1, 2, 3);
fn(1, 2, 3, 4, 5);

案例:利用函数求任意个数的最大值

function getMax() {
    // arguments = [1, 2, 3]
    var max = arguments[0];
    for (var i = 1; i < arguments.length; i++) {
        if (arguments[i] > max) {
            max = arguments[i];
        }
    }
    return max;
}
console.log(getMax(1, 2, 3));
console.log(getMax(1, 2, 3, 4, 5));
console.log(getMax(11, 2, 34, 555, 6, 100));

函数案例:

案例:利用函数封装方式,翻转任意一个数组

function reverse(arr) {
    var newArr = [];
    for (var i = arr.length - 1; i >= 0; i--) {
        newArr[newArr.length] = arr[i];
    }
    return newArr;
}
var arr1 = reverse([1, 2, 3, 4, 5, 6]);
console.log(arr1)

案例:冒泡排序

function sort(arr) {
    for (var i = 0; i < arr.length -1; i++) {
        for (var j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j+1]) {
                var temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    return arr;
}
var arr1 = sort([1, 4, 2, 9, 8]);
console.log(arr1);

案例:判断闰年

要求:输入一个年份,判断是否是年(闺年:能被4整除并且不能被100整数,或者能被400整除。

function isRunYear(year) {
    var flag = false;
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
        flag = true;
    }
    return flag;
}
console.log(isRunYear(2000));
console.log(isRunYear(1999));

案例:函数可以调用另一个函数

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

function fn1() {
    console.log(111);
    fn2();
    console.log('fn1');
}
function fn2() {
    console.log(222);
    console.log('fn2');
}
fn1();

案例:用户输入年份,输出当前年份2月份的天数

如果是闰年,则2月份是29天,如果是平年,则2月份是28天

function backDay() {
    var year = prompt('请您输入年份:');
    if (isRunYear(year)) {
        alert('当前年份是闰年2月份有29天');
    } else {
        alert('当前年份是平年2月份有28天');
    }
}

function isRunYear(year) {
    var flag = false;
    if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
        flag = true;
    }
    return flag;
}

backDay();

函数的两种声明方式

  • 利用函数关键字自定义函数(命名函数)
  • 函数表达式(匿名函数)
// 1. 利用函数关键字自定义函数(命名函数);
function fn() {

}
fn();
// 2. 函数表达式(匿名函数)
// var 变量名 = function() {};
var fun = function (aru) {
    console.log('我是函数表达式');
    console.log(aru);
}
fun('九少');
// (1) fun是变量名 不是函数名
// (2) 函数表达式声明方式跟声明变量差不多,只不过变量里面存的是值 而 函数表达式里面存的是函数
// (3) 函数表达式也可以进行传递参数

 


 JavaScript作用域

  • 作用域
  • 变量的作用域
  • 作用域链

 作用域

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

// 1. JavaScript作用域:就是代码名字(变量)在某个范围内起作用和效果,目的是为了提高程序的可靠性,更重要的是减少命名冲突
// 2. js的作用域(es6)之前:全局作用域 局部作用域
// 3. 全局作用域:整个script标签 或者是一个单独的js文件
var num = 20;
console.log(num);

// 4. 局部作用域(函数作用域) 在函数内部就是局部作用域 这个代码的名字只在函数内部起效果和作用
function fn() {
    // 局部作用域
    var num = 10;
    console.log(num);
}
fn();

 变量的作用域

在JavaScript中,根据作用域的不同,变量分为两种:

  • 全局变量
  • 局部变量

 1)全局变量

在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。

  • 全局变量在代码的任何位置都可以使用
  • 在全局变量作用域下var声明的变量是全局变量
  • 特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)

 2)局部变量

在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)

  • 局部变量只能在该函数内部使用
  • 在函数内部var声明的变量是局部变量
  • 函数的形参实际上就是局部变量
// 1. 全局变量:在全局作用域下的变量 在全局下都可以使用
// 注意:如果在函数内部 没有声明直接赋值的变量也属于全局变量
var num = 10;
console.log(num);

function fn() {
    console.log(num);
}
fn();
// 2. 局部变量 在局部作用域下的变量  后者在函数内部的变量就是 局部变量
// 注意:函数的形参也可以看作是局部变量
function fun() {
    var num1 = 10; // num1就是局部变量  只能在函数内部使用
    num2 = 20; // 全局变量
}
fun();
// console.log(num1);
console.log(num2);

3)从执行效率来看全局变量和局部变量

  1. 全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
  2. 局部变量 当我们程序执行完毕就会销毁

4)JS没有块级作用域

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

if (3 < 5) {
    var num = 10;
}
console.log(num); // 仍然可以在 块{} 之外使用num

作用域链

  • 只要是代码,就至少有一个作用域
  • 写在函数内部的局部作用域
  • 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
  • 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链
// 作用域链:内部函数访问外部函数的变量,采取的是链式查找的方式来决定取那个值,这种结构我们称之为作用域链。就近原则
var num = 10;

function fn() { // 外部函数
    var num = 20;

    function fun() { // 内部函数
        console.log(num);
    }
    fun();
}
fn();

案例:

function f1() {
    var num = 123;

    function f2() {
        console.log(num);
    }
    f2();
}
var num = 456;
f1(); // 输出结果为123

 案例:

var a = 1;
function fn1() {
    var a = 2;
    var b = '22';
    fn2();
    function fn2() {
        var a = 3;
        fn3();
        function fn3() {
            var a = 4;
            console.log(a);
            console.log(b);
        }
    }
}
fn1(); // 输出结果4 22

JavaScript预解析

  •  预解析
  • 变量预解析和函数预解析
  • 预解析案例

预解析

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

1)JS引擎运行JS分为两步:预解析 、代码执行

  1. 预解析JS引擎会把JS里面所有的var 还有function 提升到当前作用域的最前面
  2. 代码执行 按照代码书写的顺序从上往下执行

2)预解析分为 变量预解析(变量提升)和 函数预解析(函数提升)

  1. 变量提升 就是把所有的变量声明提升到当前作用域的最前面  不提升赋值操作
  2. 函数提升 就是把所有的函数声明提升到当前作用域的最前面  不调用函数

i. 变量提升

// 情况1
console.log(num); // undefined
var num = 10;
// 相当于执行了以下代码
var num;
console.log(num);
num = 10;
// 情况3
fun();
var fun = function () {
    console.log(22);
}
// 相当于执行了以下代码
var fun;
fun();
fun = function () {
    console.log(22);
}

注意:函数表达式 调用必须写在函数表达式的下面

ii. 函数提升

// 情况2
fn();
function fn() {
    console.log(11);
}
// 相当于执行了
function fn() {
    console.log(11);
}
fn();

预解析案例

案例1

输出结果:undefined

var num = 10;
fun();

function fun() {
    console.log(num);
    var num = 20;
}
// 相当于执行了下面的操作
var num;

function fun() {
    var num;
    console.log(num);
    num = 20;
}
num = 10;
fun();

案例2

输出结果:

undefined

20

var num = 10;

function fn() {
    console.log(num);
    var num = 20;
    console.log(num);
}
fn();
// 相当于以下代码
var num;
function fn() {
    var num;
    console.log(num);
    num = 20;
    console.log(num);
}
num = 10;
fn();

案例3

输出结果:

undefined

9

var a = 18;
f1();

function f1() {
    var b = 9;
    console.log(a);
    console.log(b);
    var a = '123';
}
// 相当于以下代码
var a;
function f1() {
    var b;
    var a;
    b = 9;
    console.log(a);
    console.log(b);
    a = '123';
}
a = 18;
f1();

案例4

输出结果:

f1();
console.log(c);
console.log(b);
console.log(a);

function f1() {
    var a = b = c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}
// 相当于以下代码
function f1() {
    var a;
    a = b = c = 9;
    // 相当于 var a = 9; b = 9; c = 9; 这里的b和c直接赋值,没有var声明,当全局变量看
    // 集体声明 var a = 9, b = 9 , c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);

JavaScript对象

  • 对象
  • 创建对象的三种方式
  • new关键字
  • 遍历对象属性

 对象

现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人可以是“对象”,一个数据库、一张网页、一个与远程服务器的连接也可以是“对象”。

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

对象是由属性和方法组成的。

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

1)为什么需要对象

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

例如:将‘张三丰’的个人信息保存在数组中的方式为:

var arr = ['张三丰', '男', 128, 154];

JS中的对象表达结构更清晰,更强大。张三丰的个人信息在对象中的表达结构如下:

张三丰.姓名 = '张三丰';
张三丰.性别 = '男';
张三丰.年龄 = 128;
张三丰.身高 = 154;
person.name = '张三丰';
person.sex = '男';
person.age = 128;
person.height = 154;

创建对象的三种方式

在JavaScript中,现阶段我们可以采用三种方式创建对象(object);

  • 利用字面量创建对象
  • 利用new Object创建对象
  • 利用构造函数创建对象

1)利用对象字面量创建对象

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

{}里面采取键值对的形式表示

  • 键:相当于属性名
  • 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型、函数类型等)

对象的调用

  • 对象里面的属性调用:对象.属性名,这个点,就理解为“的”
  • 对象里面属性的另一种调用方式:对象['属性名'],注意方括号里面的属性必须加引号,我们后面会用
  • 对象里面的方法调用:对象.方法名(),注意这个方法名字一定加括号
var obj = {
    uname: '张三丰',
    age: 18,
    sex: '男',
    sayHi: function() {
        console.log('hi~');
    }
}
// (1) 里面的属性或者方法我们采取键值对的形式 键 属性名 : 值 属性值
// (2) 多个属性或者方法中间用逗号隔开
// (3) 方法冒号后面跟的是一个匿名函数
// 2. 使用对象
// (1) 调用对象的属性 我们采取 对象名.属性名
console.log(obj.uname);
// (2) 调用属性还有一种方法 对象名['属性名']
console.log(obj['age']);
// (3) 调用对象的方法 sayHi 对象名.方法名
obj.sayHi();

变量、属性、函数、方法总结

变量和属性的相同点:它们都是用来存储数据的

不同点:

  • 变量单独声明并赋值,使用的时候直接写变量名 单独存在
  • 属性 在对象里面的不需要声明的,使用的时候必须是 对象.属性

函数和方法的相同点:都是实现某种功能  做某件事

不同点:

  • 函数是单独声明并且调用的 函数名() 单独存在的
  • 方法在对象里面调用的时候 对象.方法()

2)利用new Object创建对象  

var obj = new Object(); // 创建了一个空的对象
obj.uname = '张三丰';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function () {
    console.log('hi~');
}
// (1) 我们是利用 等号 = 赋值 的方法 添加对象的属性和方法
// (2) 每个属性和方法之间 分号隔开
console.log(obj.uname);
console.log(obj['sex']);
obj.sayHi();

3)利用构造函数创建对象

构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new 运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

语法格式:

function 构造函数名() {
    this.属性 = 值;
    this.方法 = function() {}
}

new 构造函数名()

function Star(uname, age, sex) {
    this.name = uname;
    this.age = age;
    this.sex = sex;
    this.sing = function(sang) {
        console.log(sang);
    }
}
var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
// console.log(typeof ldh);
console.log(ldh.name);
console.log(ldh['sex']);
ldh.sing('冰雨');

var zxy = new Star('张学友', 19, '男');
console.log(zxy.name);
console.log(zxy.age);
zxy.sing('李香兰');
  • 构造函数名 字首字母要大写
  • 构造函数不需要return 就可以返回结果
  • 调用构造函数 必须使用new
  • 只要new Star() 调用函数就创建一个对象
  • 属性和方法前面必须添加 this

4)构造函数和对象

构造函数:如Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class)。

创建对象:如 new Stars(),特指某一个,通过new关键字创建对象的过程我们称之为对象实例化

// 构造函数和对象
// 1. 构造函数 明星 泛指的某一大类 它类似于java语言里面的 类(class)
function Star(uname, age, sex) {
    this.name = uname;
    this.age = age;
    this.sex = sex;
    this.sing = function(sang) {
        console.log(sang);
    }
}
// 2. 对象 特指 是一个具体的事物 刘德华 == {name: '刘德华', age: 18, sex: '男', sing: ƒ}
var ldh = new Star('刘德华', 18, '男'); // 调用函数返回的是一个对象
console.log(ldh);
// 3. 我们利用构造函数创建对象的过程我们也称为对象的实例化

new关键字

new在执行时会做四件事情:

  1. 在内存中创建一个新的空对象
  2. 让this指向这个新的对象
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要return) 

遍历对象属性

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

// 遍历对象
var obj = {
    name: 'pink老师',
    age: 18,
    sex: '男',
    fn: function() {}
}
for (var k in obj) {
    console.log(k); // k 变量输出 得到的是 属性名
    console.log(obj[k]); // obj[k] 得到的是 属性值
}

小结:

对象可以让代码结构更加清晰

  1. 对象复杂数据类型Object
  2. 本质:对象就是一组无序的相关属性的方法的集合
  3. 构造函数泛指某一大类,比如苹果,不管是红苹果还是绿苹果,都统称为苹果
  4. 对象实例特指一个事物,比如苹果
  5. for ... in语句用于对对象的属性进行循环操作 

 


 

JavaScript内置对象

  • 内置对象
  • 查文档
  • Math对象
  • 日期对象
  • 数组对象
  • 字符串对象

内置对象

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

  • 前面两种对象是JS基础内容,属于ECMAScript;第三个浏览器对象属于我们JS独有的,后面JS API讲解。
  • 内置对象就是指JS语言自带的一些对象,这些对象供开发者使用,并提供了一些常用的或是最基本而必要的功能(属性和方法)
  • 内置对象最大的优点就是帮助我们快速开发
  • JavaScript提供了多个内置对象:Math、 Date、Array、String等

 查文档

MDN

Mozilla开发者网络(MDN)提供了有关开放网络技术(Open Web)的信息,包括HTML、CSS和万维网及HTML5应用的API。

MDN: https://developer.mozilla.org/zh-CN

如何学习对象中的方法

  1. 查阅该方法的功能
  2. 查看里面参数的意义和类型
  3. 查看返回值的意义和类型
  4. 通过demo进行测试

Math对象

Math 对象不是构造函数,它具有数学常数和函数的属性和方法。跟数学相关的运算(求绝对值,取整、最大值等)可以使用Math中的成员。

// Math数学对象,不是一个构造函数,所以我们不需要new来调用,而是直接使用里面的属性和方法即可
console.log(Math.PI); // 3.141592653589793
console.log(Math.max(1, 99, 3)); // 99
console.log(Math.max(-1, -10)); // -1
console.log(Math.max(1, 99, '九少')); // NaN
console.log(Math.max()); // -Infinity

案例:封装自己的数学对象

利用对象封装自己的数学对象里面有PI最大值和最小值

var myMath = {
    PI: 3.1415926,
    max: function () {
        var max = arguments[0];
        for (var i = 1; i < arguments.length; i++) {
            if (arguments[i] > max) {
                max = arguments[i];
            }
        }
        return max;
    },
    min: function () {
        var min = arguments[0];
        for (var i = 1; 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的几个常见方法

  

// 1. 绝对值方法
console.log(Math.abs(-1)); // 1
console.log(Math.abs('-1')); // 隐式转换 会把字符串型 -1  转换为数字型
console.log(Math.abs('九少')); // NaN
// 2. Math.floor() 地板 向下取整 往小了取值
console.log(Math.floor(1.1)); // 1
console.log(Math.floor(1.9)); // 1
// 3. Math.ceil()   ceil 天花板 向上取整 往大了取值
console.log(Math.ceil(1.1)); // 2
console.log(Math.ceil(1.9)); // 2
// 4. Math.round()  四舍五入
console.log(Math.round(1.1)); // 1
console.log(Math.round(1.5)); // 2
console.log(Math.round(-1.1)); // -1
console.log(Math.round(-1.5)); // 这个结果是 -1 ,其他数字都是四舍五入,但是.5特殊,它往大了取
// 1. Math对象随机数方法 random()返回一个随机的小数 0 <= x < 1
// 2,这个方法里面不跟参数
console.log(Math.random()); // 返回0~1之间的一个数
// 3. 得到两个数之间的随机整数并且包含这2个整数
// 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));
// 随机点名
var arr = ['张三', '张三丰', '李四', '李思思'];
console.log(arr[getRandom(0, 3)]);
console.log(arr[getRandom(0, arr.length - 1)]);

案例:猜数字游戏

程序随机生成一个1~10之间的数字,并让用户输入一个数字

  1. 如果大于该数字,就提示,数字大了,继续猜
  2. 如果小于该数字,就提示数字小了,继续猜
  3. 如果等于该数字,就提示猜对了,结束程序

案例分析:

  1. 随机生成一个1~10的整数,我们需要用到Math.random()方法
  2. 需要一直猜到正确为止,所有需要一直循环,使用while循环
  3. 核心算法:使用if else if多分支语句来判断大于、小于、等于
function getRandom(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}
var random = getRandom(1, 10);
while (true) {
    var num = prompt('你来猜? 输入1~10之间的一个数字');
    if (num > random) {
        alert('你猜大了');
    } else if (num < random) {
        alert('你猜小了');
    } else {
        alert('恭喜你猜对了!');
        break; // 退出整个循环结束程序
    }
}

日期对象

  • Date对象和Math对象不一样,他是一个构造函数,所以我们需要实例化后才能使用
  • Date实例用来处理日期和时间

Date()方法的使用

1)获取当前时间必须实例化

var now = new Date();
console.log(now);

2)Date()构造函数的参数

如果括号里面有时间,就返回参数里面的时间。例日期格式字符串为“2019-5-1’,可以写成newDate('2019-5-1')或者 new Date('2019/5/1')
日期格式化

我们想要2019-8-88:8:8格式的日期,要怎么办?

需要获取日期指定的部分,所以我们要手动的得到这种格式

var date = new Date();
console.log(date.getFullYear()); // 返回当前日期的年
console.log(date.getMonth() + 1); // 月份 返回的月份小1个月 记得月份+1
console.log(date.getDate()); // 返回的是 几号
console.log(date.getDay()); // 周一返回的是1 周六返回的是6 但是周日返回的是0
// 输出一个 xxxx年 x月 x日 星期x
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
var day = date.getDay();
console.log('今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day]);
// 格式化日期 时分秒
var date = new Date();
console.log(date.getHours());
console.log(date.getMinutes());
console.log(date.getSeconds());
// 要封装一个函数返回当前的时分秒 格式 08:08:08
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());

获取日期的总的毫秒形式

Date对象是基于1970年1月1日(世界标准时间)起的毫秒数。我们经常利用总的毫秒数来计算时间,因为它更精确。

// 1.通过valueOf() getTime()
var date = new Date();
console.log(date.valueOf()); // 就是我们现在时间距离1970.1.1总的毫秒数
console.log(date.getTime());
// 2.简单的写法(常用写法)
var date1 = +new Date(); // +new Date() 返回的就是总的毫秒数
console.log(date1);
// 3. H5 新增的 获得总的毫秒数
console.log(Date.now());

案例:倒计时

  1. 核心算法:输入的时间减去现在的时间就是剩余的时间,即倒计时,但是不能拿着时分秒相减,比如05分减去25分,结果会是负数的。
  2. 用时间戳来做。用户输入时间总的毫秒数减去现在时间的总的毫秒数,得到的就是剩余时间的毫秒数。
  3. 把剩余时间总的毫秒数转换为天、时、分、秒(时间戳转换为时分秒)

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); //
    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('2023-8-10 18:00:00'));

数组对象

1)数组对象的创建

创建数组对象的两种方式

  • 字面量方式
  • new Array()
// 1. 利用数组字面量
var arr = [1, 2, 3];
console.log(arr[0]);
// 2. 利用new Array()
// var arr1 = new Array(); // 创建了一个空的数组
// var arr1 = new Array(2); // 这个2 表示数组的长度为2 里面有2个空的数组元素
var arr1 = new Array(2, 3); // 等价于[2, 3] 这样写表示 里面有2个数组元素 是 2 和 3
console.log(arr1);

2)检测是否为数组

// 翻转数组
function reverse(arr) {
    if (arr instanceof Array){  // instanceof 运算符 它可以用来检测是否为数组
        var newArr = [];
        for (var i = arr.length - 1; i >= 0; i--) {
            newArr[newArr.length] = arr[i];
        }
        return newArr;
    } else {
        return 'error 这个参数要求必须是数组格式 [1, 2, 3]'
    }
}
console.log(reverse([1, 2, 3]));
console.log(reverse(1, 2, 3));
// Array.isArray(参数);
var arr = [];
console.log(Array.isArray(arr));

3)添加删除数组元素的方法

// 1. push() 在数组末尾 添加一个或者多个数组元素 push推
// (1) push()参数直接写 数组元素九可以了
// (2) push完毕,返回的结果是新数组的长度
// (3) 原数组也会发生变化
var arr = [1, 2, 3];
arr.push(4, '九少');
console.log(arr);
console.log(arr.push(4, '九少')); // 5+2=7
// 2. unshift 在数组的开头 添加一个或多个数组元素
console.log(arr.unshift('red', 'purple'));
console.log(arr);
// 3. pop() 它可以删除数组的最后一个元素
console.log(arr.pop());
console.log(arr);
// 4. shift() 它可以删除数组的第一个元素
console.log(arr.shift());
console.log(arr);

案例:筛选数组

有一个包含工资的数组[1500,1200,2000,2100,1800],要求把数组中工资超过2000的删除,剩余的放到新数组里面。

var arr = [1500, 1200, 2000, 2100, 1800];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
    if (arr[i] < 2000) {
        newArr.push(arr[i]);
    }
}
console.log(newArr);

案例:数组排序

// 1. 翻转数组
var arr = ['pink', 'red', 'blue'];
arr.reverse();
console.log(arr);
// 2. 数组排序(冒泡排序)
var arr1 = [3, 4, 7, 1]; // [1, 13, 4, 7, 77]无法排序
arr1.sort();
console.log(arr1);
var arr2 = [1, 13, 4, 7, 77];
arr2.sort(function (a, b) {
    // return a - b; 升序的顺序排序
    return b - a; // 降序的顺序排序
})
console.log(arr2);

4)数组索引的方法

// 返回数组元素索引号方法 indexOf(数组元素) 作用就是返回该数组元素的索引号
// 它只返回第一个满足条件的索引号
// 它如果在该数组里面找不到元素,则返回的是-1
// var arr = ['red', 'green', 'blue', 'pink', 'blue'];
var arr1 = ['red', 'green', 'pink'];
console.log(arr1.indexOf('blue'));
var arr2 = ['red', 'green', 'blue', 'pink', 'blue'];
console.log(arr2.lastIndexOf('blue'));

案例:数组去重

有一个数组['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b'],要求去除数组中重复的元素。

案例分析:

  1. 目标:把旧数组里面不重复的元素选取出来放到新数组中,重复的元素只保留一个,放到新数组中去重。
  2. 核心算法:我们遍历日数组,然后拿着旧数组元素去查询新数组,如果该元素在新数组里面没有出现过,我们就添加否则不添加。
  3. 我们怎么知道该元素没有存在?利用新数组indexOf(数组元素)如果返回时-1 就说明新数组里面没有改元素
function unique(arr) {
    var newArr = [];
    for (var i = 0; i < arr.length; i++) {
        if (newArr.indexOf(arr[i]) === -1) {
            newArr.push(arr[i]);
        }
    }
    return newArr;
}
var demo = unique(['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b']);
console.log(demo);

案例:数组转字符串

// 1. toString() 将数组转化为字符串
var arr = [1, 2, 3];
console.log(arr.toString()); // 1, 2, 3
// 2. join(分隔符)
var arr1 = ['green', 'blue', 'pink'];
console.log(arr1.join());
console.log(arr1.join('-')); 

字符串对象

1) 基本包装类型

为了方便操作基本数据类型,JavaScript还提供了三个特殊的引用类型:String、Number和Boolean。

基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。

// 基本包装类型
var str = 'andy';
console.log(str.length);
// 对象才有属性和方法 即复杂数据类型才有属性和方法
// 简单数据类型为什么有length属性呢?
// 基本包装类型:就是把简单数据类型包装成为了 复杂数据类型
// (1) 把简单数据类型包装为复杂数据类型
var temp = new String('andy');
// (2) 把临时变量的值给 str
str = temp;
// (3)销毁这个临时变量
temp = null;

2)字符串的不可变

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

var str = 'abc';
str = 'hello';
// 当重新给str赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str1 = '';
for (var i = 0; i < 100000; i++) {
    str += i;
}
console.log(str); // 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间

3)根据字符返回位置

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

// 根据字符返回位置  str.indexOf('要查找的字符', [起始的位置])
var str = '只要肝不死那就继续肝';
console.log(str.indexOf('肝'));
console.log(str.indexOf('肝', 3)); // 索引是从3的位置开始往后查找

案例:返回字符的位置

查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数。

  1. 核心算法:先查找第一个o出现的位置
  2. 然后只要indexOf返回的结果不是-1就继续往后查找
  3. 因为indexOf只能查找到第一个,所以后面的查找,利用第二个参数,当前索引加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); // 从index+1开始往后找
}
console.log('o出现的次数是:' + num);

4)根据位置返回字符

// 1. charAt(index) 根据位置返回字符串
var str = 'andy';
console.log(str.charAt(3));
// 遍历所有的字符
for (var i = 0; i < str.length; i++) {
    console.log(str.charAt(i));
}
// 2. charCodeAt(index) 返回相应索引号的字符ASCII值 目的:判断用户按下了那个键
console.log(str.charCodeAt(0)); // 97
// 3. str[index] H5新增的
console.log(str[0]); // a

案例:返回字符位置

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

  1. 核心算法: 利用 charAt()遍历这个字符串
  2. 把每个字符都存储给对象, 如果对象没有该属性,就为1,如果存在了就 +1
  3. 遍历对象,得到最大值和该字符
var str = 'abcoefoxyozzopp';
var o = {};
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 ch = '';
for (var k in o) {
    if (o[k] > max) {
        max = o[k];
        ch = k;
    }
}
console.log('最多的字符是' + ch);

5)字符串操作方法

// 1. concat('字符串1', '字符串2'....)
var str = 'andy';
console.log(str.concat('red'));
// 2. substr('截取的起始位置', '截取第几个字符');
var str1 = '改革春风吹满地';
console.log(str1.substr(2, 2)); // 第一个2是索引号的2 第二个2是取几个字符
// 3.替换字符 replace('被替换的字符', '替换为的字符') 它只会替换第一个字符
var str = 'andy';
console.log(str.replace('a', 'b'));
//  有一个字符串’abcoefoxyozzopp‘要求把里面所有的 o 替换为 *
var str1 = 'abcoefoxyozzopp';
while (str1.indexOf('o') !== -1) {
    str1 = str1.replace('o', '*');
}
console.log(str1)
// 4. 字符转换为数组 split('分隔符')
var str2 = 'red,pink,blue';
console.log(str2.split(','));
var str3 = 'red&pink&blue';
console.log(str3.split('&'));

JavaScript简单数据类型与复杂数据类型

  • 简单类型与复杂类型
  • 堆和栈
  • 简单类型的内存分配
  • 复杂类型的内存分配
  • 简单类型传参
  • 复杂类型传参

简单类型与复杂类型

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

  • 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型 
    • string , number, boolean, undefined, null
  •  引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
    • 通过new 关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等  

堆和栈

 堆栈空间分配区别:

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

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

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

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

注意:JavaScript中没有堆栈的概念,通过堆栈的方式,可以让大家更容易理解代码的一些执行方式,便于将来学习其他语言;

简单类型的内存分配 

  • 值类型(简单数据类型):string,number,boolean,undefined,null
  • 值类型变量的数据直接存放在变量(栈空间)中 

复杂类型的内存分配 

  • 引用类型(复杂数据类型):通过new 关键字创建的对象(系统对象、自定义对象)如ObiectArray、Date等
  • 引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间中 

简单数据类型传参

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

function fn(a) {
    a++;
    console.log(a);
}
var x = 10;
fn(x);
console.log(x);

 复杂类型传参

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

// 复杂数据类型传参
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. 这个输出什么?张学友

 

学习来自:https://www.bilibili.com/video/BV1ux411d75J?p=190&spm_id_from=pageDriver&vd_source=8bc93faaf8c822f06e3c15f0c4d2e6d3

posted @ 2023-08-10 02:08  天才九少  阅读(20)  评论(0编辑  收藏  举报