该系列文章是博主学习前端入门课程的笔记,同时也为了方便查阅,有任何错漏或疑问都欢迎在评论区提出。本文介绍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.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. 在哪查
通过文档可以查询到内置对象的属性和方法,也是程序员学习知识的必备途径之一。前端常用的文档有MDN和W3C,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); // 张学友