【JavaScript】作用域、预解析、对象、内置对象、简单类型与复杂类型的内存分配和传参

该系列文章是博主学习前端入门课程的笔记,同时也为了方便查阅,有任何错漏或疑问都欢迎在评论区提出。本文介绍JS的作用域、预解析、对象、内置对象、简单类型与复杂类型的内存分配和传参

思维导图

在这里插入图片描述

第一章 作用域

1.1 作用域概述

作用域是变量起作用和效果的范围,目的是提高程序的可靠性,为了减少命名冲突。
JavaScript中有两种作用域(ES6之前没有块级作用域):全局作用域和局部作用域。

1.2 全局作用域

在整个代码执行环境(script标签或者js文件)中作用

// 在同一个作用域,声明同一个变量名会出现冲突
var num = 10;
var num = 30;
console.log(num); // 30

1.3 局部(函数)作用域

在函数内部就是局部作用域,变量只在函数内部起效果和作用

var num = 10;
function fn() {
    // 局部作用域
    var num = 20;
    console.log(num);
}
var num = 30;
console.log(num); // 30
fn(); // 20

1.4 变量的作用域

根据作用域的不同分为全局变量和局部变量

1. 全局变量

在全局作用域下的变量,在全局都可以使用

var num = 10;
console.log(num); // 10

function fn() {
    console.log(num);
}
fn(); // 10

2. 局部变量

在局部作用域(函数)内的变量,只能在函数内部使用

function fun(aru) {
    var num1 = 10;
    num2 = 20;
}
fun();
console.log(num1); // aru is not defined(报错),num1是局部变量 只能在函数内部使用
console.log(num2); // 20,如果函数内的变量没有声明就赋值,会变成全局变量
console.log(aru); // aru is not defined(报错),函数的形参也是局部变量

3. 总结

  1. 全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存
  2. 局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后就会被销毁,因此更节省内存

1.5 作用域链

当在 Javascript 中使⽤⼀个变量的时候,⾸先 Javascript 引擎会尝试在当前作⽤域下去寻找该变量,如果没找到,再到它的上层作⽤域寻找,以此类推直到找到该变量或是已经到了全局作⽤域。

示例

function f1() {
    var num = 123;
    function f2() {
        console.log(num);
    }
    f2();
}
var num = 456;
f1(); // 123

作用域链
在这里插入图片描述

第二章 预解析

2.1 概念

JavaScript 解析器在运行 JavaScript 代码的时候分为两步:预解析和代码执行。

  • 预解析:JS 代码执行之前,浏览器会默认把var 和 function 声明的变量在内存中进行提升到当前作用域的最前面进行声明。
  • 代码执行: 从上到下执行JS语句。

2.2 变量预解析(变量提升)

变量的声明会被提升到当前作用域的最上面,变量的赋值不会提升

代码示例

console.log(num1); // undefined,没有报错
var num1 = 10;

// 等价于
var num1;
console.log(num1);
num1 = 10;

2.3 函数预解析(函数提升)

把所用的函数声明提升到当前作用域最前面,函数调用不提升

代码示例

fn(); // 11
function fn() {
    console.log(11);
}

// 等价于
function fn() {
    console.log(11);
}
fn();

注意: 函数表达式会执行变量提升,但是此时接收函数的变量名无法正确的调用

fun(); // 报错 fun is not a function
var fun = function () {
    console.log(22);
}

// 函数表达式必须在调用之前
// 等价于
var fun;
fun();
fun = function () {
    console.log(22);
}

2.4 案例

案例1

var num = 10;
fun(); // undefined

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

// 等价于
var num;
function fun() {
    var num;
    console.log(num);
    num = 20;
}

num = 10;
fun();

案例2

f2();
console.log(e); // 9 
console.log(d); // 9
console.log(c); // 报错

function f2() {
    var c = d = e = 9;
    // 相当于
    // var c = 9; d = 9; e = 9; // d和e只赋值未声明,是全局变量
    console.log(c); // 9
    console.log(d); // 9
    console.log(e); // 9
}

// 等价于
function f2() {
    var c;
    c = d = e = 9;
    console.log(c);
    console.log(d);
    console.log(e);
}
f2();
console.log(e);
console.log(d);
console.log(c);

输出
在这里插入图片描述

第三章 对象

3.1 对象的概念

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

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的功能,在对象中用方法来表示(常用动词)
    在这里插入图片描述

3.2 为什么需要对象?

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

例如:张三疯在数组中的存储形式为

var arr = [‘张三疯’, ‘男', 128,154];

上面用数组保存数据存在缺点:数据只能通过索引值访问,开发者需要了解所有数据的分布才能准确地获取数据,而当数据量庞大时,不可能做到记忆所有数据的索引值。
为了让更好地存储一组数据,对象应运而生:对象中为每项数据设置了属性名称,可以使访问数据更语义化,数据结构清晰,方便开发者使用。

var obj = {
	name: "张三疯",
	sex: "男",
	age: 128,
	height: 154,
}

3.3 创建对象的三种方式

1. 利用对象字面量

{}包含对象的属性和方法,采取键值对的形式表示,键值对之间用,(英文逗号)分隔。

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

代码示例

// 声明对象
var obj = {}; // 创建了空的对象

var obj1 = {
    name: '张三峰',
    sex: '男',
    age: 18,
    // 方法后面跟的是匿名函数
    sayHi: function () {
        console.log('Hi');
    }
}

// 调用对象
// a.调用对象的属性:对象名.属性名 可以将.理解为的
console.log(obj1.name); // 张三峰
// b.调用属性还有一种方法:对象名['属性名'] 属性名要加引号
console.log(obj1['age']); // 18
// c.调用对象的方法:对象名.方法名() 注意有括号
obj1.sayHi(); // Hi

2. 利用new Object

通过内置构造函数Object创建对象

var obj = new Object(); // 创建一个空的对象
// 利用 = 赋值的方法添加对象的属性和方法
// 属性或方法之间用分号分隔
obj.uname = '张三峰';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function () {
    console.log('Hi');
};

// 调用对象的方法还是一样
console.log(obj.uname);
console.log(obj.age);
obj.sayHi();

3.利用构造函数

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

语法格式

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

代码示例

// 创建四大天王的对象 相同的属性:名字、性别、年龄 相同的方法:唱歌
// 构造函数,如 Stars(),抽象了对象的公共部分,封装到了函数里面,它泛指某一大类(class)

function Star(uname, sex, age) {
    this.name = uname;
    this.sex = sex;
    this.age = age;
    this.sing = function (song) {
        console.log(song);
    }
}

// 创建对象,特指某一个,通过 new 关键字创建对象的过程我们也称为对象实例化
var ldh = new Star('刘德华', '男', 18); // 调用函数返回的是对象
console.log(ldh); 
console.log(typeof ldh); // object
console.log(ldh.name); // 刘德华
console.log(ldh['age']); // 18
ldh.sing('冰雨'); // 冰雨

var zxy = new Star('张学友', '男', 19);
console.log(zxy.name); // 张学友
console.log(zxy.sex); // 男
zxy.sing('李香兰'); // 李香兰

输出
在这里插入图片描述
注意

  • 构造函数的名字首字母要大写(不是硬性规定,但最好遵循)
  • 构造函数不需要return即可返回结果
  • 使用构造函数创建对象需要new来调用
  • 构造的属性和方法前面都要加this

3.4 变量属性、函数方法的区别

  • 声明: 属性和方法是对象的一部分,不需要声明。变量单独声明赋值,作为存储数据的容器;函数单独声明,作为封装操作的容器
  • 调用: 属性和方法前都要加上对象名,变量和函数则不用
var num = 10;
var obj = {
    age: 18,
    fn: function () {
        console.log('obj');
    }
}
function fun() {
    console.log('function');
}

console.log(num);
console.log(obj.age);

obj.fn()
fun()

3.5 new关键字

function Star(uname, sex, age) {
    this.name = uname;
    this.sex = sex;
    this.age = age;
    this.sing = function (song) {
        console.log(song);
    }
}

var ldh = new Star('刘德华', '男', 18); // 调用函数返回的是对象

new关键字的执行过程

  • 在构造函数代码开始执行之前,创建一个空对象{}
  • 修改this的指向,把this指向创建出来的空对象
  • 执行函数的代码
  • 在函数完成之后,返回this—即创建出来的对象(ldh)

3.6 遍历对象

利用for in语句可以遍历对象

var obj = {
	// name是javascript内置的属性,所以用作变量时可能会引起bug,尽量避免使用
    uname: 'pink老师',
    age: 18,
    sex: '男',
    fn: function () { }
}

for (var k in obj) { // 变量名常用k 或者 key
    console.log(k); // 属性名
    console.log(obj[k]); // 属性值
}

输出
在这里插入图片描述

第四章 内置对象

4.1 什么是内置对象

JavaScript 中的对象分为3种:自定义对象(本文第三章讲述的) 、内置对象、 浏览器对象。前面两种对象属于 ECMAScript, 第三个浏览器对象是JS 独有的。
内置对象就是指 JS 语言自带供开发者使用的对象,提供常用的或是必要的功能(属性和方法)

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

4.2 查文档

1. 在哪查

通过文档可以查询到内置对象的属性和方法,也是程序员学习知识的必备途径之一。前端常用的文档有MDNW3C,Mozilla 开发者网络(MDN)提供了有关开放网络技术(Open Web)的信息,包括 HTML、CSS 和万维网及HTML5 应用的 API

2. 怎么查

  • 查阅该方法的功能
  • 查看参数的意义和类型
  • 查看返回值的意义和类型
  • 通过 demo 进行测试

4.3 Math数学对象

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

在这里插入图片描述

1. 圆周率和最大值

console.log(Math.PI); // 属性:圆周率
console.log(Math.max(1, 78, 879)); // 方法:求最大值
console.log(Math.max(123, 3231, 'pink老师')); // NaN,存在非数字
console.log(Math.max()); // -Infinity

2. 封装自己的数学对象

可根据题目先自行编写,以加深对数学对象的理解

// 写一个myMath对象:包含PI、最大值和最小值
var myMath = {
    PI: 3.141592653589793,
    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); // 3.141592653589793
console.log(myMath.max(1, 234, 123)); // 234
console.log(myMath.min(78, 7978, 213)); // 78

3. 绝对值和三个取整方法

// 1.取绝对值 abs()
console.log(Math.abs(1)); // 1
console.log(Math.abs(-1)); // 1
console.log(Math.abs('-1')); // 1,隐式转换
console.log(Math.abs('pink')); // NaN

// 2.向下取整 floor()
console.log(Math.floor(1.1)); // 1
console.log(Math.floor(1.9)); // 1

// 3.向上取整 ceil()
console.log(Math.ceil(1.1)); // 2
console.log(Math.ceil(1.9)); // 2

// 4.四舍五入 round()
console.log(Math.round(1.1)); // 1
console.log(Math.round(1.5)); // 2
console.log(Math.round(1.9)); // 2
// 其他数是四舍五入,.5是往大了取值
console.log(Math.round(-1.1)); // -1
console.log(Math.round(-1.5)); // -1
console.log(Math.round(-1.9)); // -2

4. 随机数方法 random()

random() 方法可以随机返回一个小数,其取值范围是 [0,1),左闭右开0 <= x < 1

代码示例

console.log(Math.random());

获取两个数之间的随机整数,且包含这两个数

function getRandom(min, max) {
	// max - min + 1这里+1是为了取到max,因为random()的取值范围是 [0,1)
    return Math.floor(Math.random() * (max - min + 1) + min);
}
console.log(getRandom(1, 10));

利用这个函数可以做随机点名

var arr = ['大宝', '江湖', '波', '河', '老板'];
console.log(arr[getRandom(0, arr.length - 1)]);

4.4 Date日期对象

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

1. Date对象的使用

// Date是日期对象,是构造函数,必须使用new来调用创建日期对象
// 1.如果没有参数,返回系统的当前时间
var date = new Date();
console.log(date); // Sat Jun 24 2023 09:42:54 GMT+0800 (中国标准时间)

// 2.参数写法 数字型 2020,11,11 字符串型 '2020-11-11 8:8:8'
var date1 = new Date('2020-11-11 8:8:8'); 
var date2 = new Date(2020, 11, 11);
console.log(date1); // 开发中常,Wed Nov 11 2020 08:08:08 GMT+0800 (中国标准时间)
console.log(date2); // 有bug,返回的是十二月,Fri Dec 11 2020 00:00:00 GMT+0800 (中国标准时间)

2. 日期格式化

想要2023年6月24日格式的日期,需要获取日期指定的部分,所以要手动得到这种格式

在这里插入图片描述
年月日

var date = new Date();
console.log(date.getFullYear()); // 返回当前日期的年份,2023
console.log(date.getMonth() + 1); // 返回的月份比实际小1,需要+1
console.log(date.getDate()); // 返回日期20
console.log(date.getDay()); // 返回3(周三),注意周日返回的是0

// 2023年6月20日星期二
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]); // 今天是2023年6月20日 星期三

时分秒

var date = new Date();
console.log(date.getHours()); // 时
console.log(date.getMinutes()); // 分
console.log(date.getSeconds()); // 秒

function getTimer() {
    var time = new Date();
    var hour = time.getHours();
    // 当时间小于10的时候在前面补0
    hour = hour > 10 ? hour : '0' + hour;
    var minute = time.getMinutes();
    minute = minute > 10 ? minute : '0' + minute;
    var second = time.getSeconds();
    second = second > 10 ? second : '0' + second;
    return hour + ':' + minute + ':' + second
}
console.log(getTimer()); // 09:45:37

3. 获取时间的毫秒数(时间戳)

Date 对象是基于1970年1月1日(世界标准时间)起的毫秒数,又称为时间戳

获取时间戳的三种方式

// 距离1970年1月1日零时总的毫秒数(时间戳)
// 1.valueof() getTime()
var date = new Date();
console.log(date.valueOf());
console.log(date.getTime());
// 2.简单的写法,开发最常用的写法
var date1 = +new Date();
console.log(date1);
// 3.h5新增的方法,存在兼容性问题
console.log(Date.now());

利用时间戳做一个倒计时

  • 分析
    在这里插入图片描述
  • 代码
function countDown(time) {
    var nowTime = +new Date();
    var inputTime = +new Date(time);

    // 剩余的总秒数
    var rest = parseInt((inputTime - nowTime) / 1000);
    console.log((rest)); // 308027
    if (rest > 0) {
        // 秒 
        var second = parseInt(rest % 60);
        second = second > 10 ? second : '0' + second;
        // 分
        var minute = parseInt(rest / 60 % 60);
        minute = minute > 10 ? minute : '0' + minute;
        // 时
        var hour = parseInt(rest / 3600 % 24);
        hour = hour > 10 ? hour : '0' + hour;
        // 天
        var day = parseInt(rest / 86400);
        day = day > 10 ? day : '0' + day;

        return '倒计时' + day + '天' + hour + '时' + minute + '分' + second + '秒';
    } else {
        return '活动开始啦'
    }
}
console.log(countDown('2023-6-27 23:21:11')); // 倒计时03天13时33分47秒
  • 输出
    在这里插入图片描述

4.5 数组对象

1. 创建数组的两种方式

// 1.利用数组字面量
var arr = [1, 2, 3]

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

2. 判断数据类型是否为数组

代码示例

// 1.instanceof 运算符
var arr = [];
var obj = {};
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false 
console.log(obj instanceof Object); // true

// 2.Array.isArray()  HTML5提供的方法
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // flase

案例

// 如果输入不是数组,翻转函数会报错
function reverse(arr) {
    // if (arr instanceof Array) {
    if (Array.isArray(arr)) {
        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])); // [3,2,1]
console.log(reverse(1, 2, 3)); // error 请输入数组,例如[1,2,3]

输出

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

在这里插入图片描述

代码示例:注意方法的功能、参数、返回值

// 1.在数组末尾添加一个或多个元素 push()
var arr = [1, 23, 3];
console.log(arr.push(4, 'pink')); // 5
console.log(arr); // [1, 23, 3, 4, 'pink']
// a.push()参数直接写数组元素即可
// b.push之后,arr.push()返回的是新数组的长度
// c.原数组会发生变化

// 2.在数组前面添加一个或多个元素 unshift()
console.log(arr.unshift('red', 'purple')); // 7
console.log(arr); // ['red', 'purple', 1, 23, 3, 4, 'pink']
// a.unshift()参数直接写数组元素即可
// b.unshift之后,arr.unshift()返回的是新数组的长度
// c.原数组会发生变化

// 3.pop() 删除数组的最后一个元素
console.log(arr.pop()); // 'pink'
console.log(arr); // ['red', 'purple', 1, 23, 3, 4]
// a.pop()没有参数
// b.pop()之后返回的是删除的那个元素
// c.原数组会发生变化

// 4.shift() 删除数组的第一个元素
console.log(arr.shift()); // 'red'
console.log(arr); // ['purple', 1, 23, 3, 4]
// a.shift()没有参数
// b.shift()之后返回的是删除的那个元素
// c.原数组会发生变化

案例

// 把小于两千的数放到新数组
var arr = [1500, 1200, 2000, 2100, 1800];
var newArr = [];

for (var i = 0; i < arr.length; i++) {
    if (arr[i] < 2000) {
        // newArr[newArr.length] = arr[i];
        newArr.push(arr[i]);
    }
}

console.log(newArr); // [1500, 1200, 1800]

4. 数组排序

在这里插入图片描述

代码示例

// 1.翻转数组
var arr = ['red', 'blue', 'pink'];
arr.reverse(); 
console.log(arr); // ['pink', 'blue', 'red']

// 2.数组排序(冒泡排序)
var arr1 = [3, 4, 22, 1];
// 加个函数是为了避免sort多位数排序出现的问题(sort排序只比较第一位数)
arr1.sort(function (a, b) {
    return a - b; // 按照升序顺序排列
    return b - a; // 按照降序顺序排列
});
console.log(arr1); // [1, 3, 4, 22]
arr1.sort();
console.log(arr1); // [1, 22, 3, 4]

5. 数组索引方法

在这里插入图片描述
代码示例

// 1.indexOf() 获取数组元素的索引号,从前面开始查找。只返回第一个满足条件的索引号
// 如果没有满足条件的元素,返回-1
var arr = ['red', 'blue', 'pink', 'skyblue', 'blue'];
console.log(arr.indexOf('blue')); // 1

// 2.lastIndexOf() 获取数组元素的索引号,从后面开始查找。只返回第一个满足条件的索引号
console.log(arr.lastIndexOf('blue')); // 4

借助索引方法可以实现数组去重

在这里插入图片描述

// 遍历旧数组,判断旧数组中的元素是否在新数组中,如果没有则存入新数组,否则不存入
var arr = ['c', 'a', 'z', 'a', 'x', 'a', 'x', 'c', 'b'];
function unique(arr) {
    var newArr = [];
    for (var i = 0; i <= arr.length - 1; i++) {
        if (newArr.indexOf(arr[i]) == -1) {
            newArr.push(arr[i]);
        }
    }
    return newArr;
}
console.log(unique(arr)); // ['c', 'a', 'z', 'x', 'b']

6. 数组转换为字符串

在这里插入图片描述

代码示例

// 1.toString()
var arr = [1, 2, 3];
console.log(arr.toString()); // 1,2,3
// String()效果也一样
console.log(String(arr)); // 1,2,3

// 2.join()
var arr1 = ['blue', 'green', 'pink'];
console.log(arr1.join()); // blue,green,pink
console.log(arr1.join('-')); // blue-green-pink
console.log(arr1.join('&')); // blue&green&pink,&符号在提交表单中常用

7. 合并数组 concat()

// 1.concat() 合并两个或多个数组,返回一个新数组,不会改变原来的数组
// 参数可以是数组,也可以是值
var arr1 = [1, 2, 3];
var arr2 = ['a', 'b', 'c'];
var arr3 = ['red', 'blue', 'pink'];

// 合并两个数组
var newArr = arr1.concat(arr2);
console.log(newArr);  // [1, 2, 3, 'a', 'b', 'c']
// 合并三个数组
var newArr1 = arr1.concat(arr2, arr3);
console.log(newArr1); // [1, 2, 3, 'a', 'b', 'c', 'red', 'blue', 'pink']
// 合并值
var newArr3 = arr1.concat('test', undefined, null, [1, 2]);
console.log(newArr3); // [1, 2, 3, 'test', undefined, null, 1, 2]

// 合并嵌套数组
var num1 = [[1]];
var num2 = [2, [3]];
var numbers = num1.concat(num2);
console.log(numbers); // [[1], 2, [3]]

8. 数组切片 slice()

// 2.slice() 对数组进行切片,原数组不改变
// 语法格式
// slice(start,end) start和end参数是数组的索引号,可选择性使用。

var animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

// 只有start,将索引号之后的元素全部提取
console.log(animals.slice(2)); // ["camel", "duck", "elephant"]
// 返回值是一个含有被提取元素的新数组

// 有start和end,提取两者之间的元素,注意是左闭右开
console.log(animals.slice(2, 4)); // ["camel", "duck"]

console.log(animals.slice(1, 5));// ["bison", "camel", "duck", "elephant"]

// 索引号小于0时,取的是(索引号index+数组长度arr.length)的位置。-2+5=3,所以是从索引号为3处开始取的
console.log(animals.slice(-2)); // ["duck", "elephant"]

console.log(animals.slice(2, -1)); // ["camel", "duck"]

// 没有start和end,提取数组所有元素
console.log(animals.slice());// ["ant", "bison", "camel", "duck", "elephant"]

9. 添加或删除数组元素 splice()

// 3.splice() 添加或删除数组元素,会改变原数组
// 语法格式
// splice(start, deleteCount, item1, item2, itemN) 除了start其他参数都可选
// start表示对数组元素进行操作的位置
// deletecount表示从start开始删除元素的个数,如果没有传参则会删除start之后的所有元素
// item表示需要添加的元素,如果没有则splice()只在数组中删除元素

// 代码示例
var months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// Inserts at index 1
console.log(months); // ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// Replaces 1 element at index 4
console.log(months); // ["Jan", "Feb", "March", "April", "May"]

// 返回值是被删除的元素

4.6 字符串对象

1. 基本包装类型

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

var str = 'andy';
console.log(str.length); // 4,字符串类型也有属性?

等价于
// 1.把简单数据类型包装成复杂数据类型
var temp = new String('andy');
// 2.把临时变量赋值给str
var str = temp;
// 3.销毁临时变量
temp = null;

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

2. 字符串的不可变性

字符串的值不可变,虽然重新赋值可以改变内容,但其实是地址变了,新数据开辟了一个内存空间,旧数据还占据着内存。所以要避免大量地拼接字符串,减少内存占用

对比以下两段代码的运行时间,可以直观感受到字符串对内存的占用

// 字符串的不可变,所以不要大量地使用/拼接字符串
str = '';
for (var i = 0; i <= 10000000; i++) {
    str += i;
}
console.log(str);
str = 1;
for (var i = 0; i <= 10000000; i++) {
    str += i;
}
console.log(str);

3. 根据字符返回位置

和数组索引方法类似

在这里插入图片描述

代码示例

// 1.str.indexOf('要查询的字符',[起始位置])
var str = '改革春风吹满地,春天来了';
console.log(str.indexOf('春')); // 2
// 从索引号3的位置往后查找
console.log(str.indexOf('春', 3)); // 8

案例:查找字符串"abcoefoxyozzopp"中所有o出现的位置以及次数
在这里插入图片描述

var str = 'abcoefoxyozzopp';
var index = str.indexOf('o');
var num = 0;
while (index != -1) {
    console.log(index);
    index = str.indexOf('o', index + 1);
    num++;
}
console.log('o出现的次数:' + num);

在这里插入图片描述

4. 根据位置返回字符

在这里插入图片描述

// 1.charAt(index) 根据位置返回字符
var str = 'andy';
console.log(str.charAt(3)); // y
// 遍历所有字符
for (var i = 0; i < str.length; i++) {
    console.log(str.charAt(i));
}

// 2.charCodeAt() 根据输入返回ASCII码  可以判断用户输入
console.log(str.charCodeAt(0)); // 97

// 3.str[] h5新增,和charAt()功能相同,但存在兼容性问题
console.log(str[0]); // a

案例:判断一个字符串 ‘abcoefoxyozzopp’ 中出现次数最多的字符,并统计其次数
在这里插入图片描述

var str = 'abcoefoxyozzopp';
var o = {};


for (var i = 0; i < str.length; i++) {
    var chars = str.charAt(i);
    if (o[chars]) {
        o[chars]++;
    } else {
        o[chars] = 1;
    }
}

var max = 0;
var most;
for (var k in o) {
    // k是属性名
    // o[k]是属性值
    if (o[k] > max) {
        max = o[k];
        most = k;
    }
}
console.log('出现次数最多的是:' + most + ';次数是:' + max);

5. 字符串操作方法

在这里插入图片描述

代码示例:只介绍两种,另外两种较相似。有兴趣可以通过MDN查询

// 1.concat(字符串1,字符串2,...) 字符串拼接,和+由于
var str = 'andy';
console.log(str.concat('red')); // andyred

// 2.substr(索引号,取的字符个数) 
var str1 = '改革春风吹满地';
console.log(str1.substr(0, 2)); // 改革,0表示从索引号为0开始取,2表示取两个字符

6. 其他方法

// 1.replace(被替换的字符,替换后的字符) 替换字符,只会替换第一个字符
var str = 'andyand';
console.log(str.replace('a', 'b'));
// 把abcoefoxyozzopp所有的o替换为*
var str1 = 'abcoefoxyozzopp';
while (str1.indexOf('o') != -1) {
    str1 = str1.replace('o', '*');
}
console.log(str1);

// 2.split() 字符串转换为数组
var str2 = 'red,pink,blue';
console.log(str2.split(',')); // [red,pink,blue]
var str2 = 'red&pink&blue';
console.log(str2.split('&')); // [red,pink,blue]

第五章 简单类型与复杂类型

5.1 概念

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

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

  • string ,number,boolean,undefined,null
// 简单数据类型 null 返回的是一个空的对象 object
var timer = null;
console.log(typeof timer);
// 如果有个变量打算存储对象,但没想好时可以赋值为null

引用类型(复杂数据类型):在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型

  • 通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等

5.2 堆和栈

1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;简单数据类型存放到栈,在里面开辟空间存放值
2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型存放到堆里面(首先在栈中存放地址[用十六进制表示],地址指向堆里面的数据)
在这里插入图片描述

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

5.3 简单类型的内存分配

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

// 传递的是值
function fn(a) {
    a++;
    console.log(a);
}
var x = 10;
fn(x); // 11
console.log(x); // 10

在这里插入图片描述

5.4 复杂类型传参

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

// 传递的是地址
function Person(name) {
    this.name = name;
}
function f1(x) {
    console.log(x.name);
    x.name = '张学友';
    console.log(x.name);
}

var p = new Person('刘德华');
console.log(p.name); // 刘德华
f1(p);
console.log(p.name); // 张学友

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君和-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值