JavaScript基础语法
这篇笔记主要讲了JavaScript函数,变量作用域,预解析,对象,内部对象以及两种数据类型
这篇笔记有很多JavaScript的特性,所以可以认真看下
这篇笔记是初学者写下的笔记,如有错误,欢迎前来指正
JavaScript函数
- 函数:重复使用某些代码,实现代码复用
- 函数命名仍然是小驼峰命名
- 函数可以调用另一个函数 (可以先使用后定义)
- 函数里面还可以嵌套函数
JavaScript函数使用
- 代码示例:
// 声明
function 函数名(形参1, 形参2) {
// 函数体
return 参数;
}
var 接收变量 = 函数名(实参1, 实参2); // 调用
-
注意:不需要声明形参,可以直接写一个参数名
-
形参的个数和实参不匹配也可以:
- 实参个数 > 形参个数:只读取形参的个数
- 实参个数 < 形参个数:没有值的形参默认为undefined
-
return语句之后的代码不会被执行
-
return只能返回一个值,返回多个值以最后一个值为准
-
想要返回多个值,可以使用数组作为返回值
-
函数没有return的话,返回的值是undefined
arguments使用
- arguments:所有函数都内置了一个arguments对象,存储了所有实参
- 可以用于所有的函数,包括对象内的函数
- 代码示例:
function test() {
console.log(arguments);
// 输出[1,2,3]
}
test(1,2,3);
- arguments展示的是伪数组,所以可以遍历
- 可以使用length属性
- 可以用下标索引
- 不同点:没有数组的 push 和 pop 方法
arguments使用案例
- 求未知个数的数组里的最大值
function getMax() {
var max = 0;
for (var i = 0; i < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1,2,3,4,7,3,4,5));
// 输出7
函数声明两种方式
- 直接声明 (function方法):
function fn() {
}
fn();
- 函数表达式 (匿名函数):
var 变量名 = function(){};
// 调用
变量名();
变量作用域
-
es6 版本之前:全局变量,局部变量
- 全局变量:作用于 script 标签之间或者一个js文件
- 局部变量:在函数内部声明的变量
-
注意:全局变量和局部变量名字可以相同,不会起冲突
-
如果在同一个作用域下名字相同,后面的值会把前面的值覆盖
-
特例!
function fun() {
var num1 = 10;
num2 = 20;
}
console.log(num1);
// 报错,变量未定义
console.log(num2);
// 可以直接输出
- 注意:在函数内部没有声明直接赋值的变量也属于全局变量
- 函数的形参也属于局部变量
块级作用域 (新属性)
- 现阶段没有块级作用域
- 在一个花括号里面的作用域
- 注意!和其他语言不同,JavaScript在if,for里面定义的变量都是可以全局使用的,只有函数里声明的变量才是局部变量
作用域链
- 内部函数可以访问外部函数
- 代码示例:
var num = 10;
function fn() {
var num = 20;
function fun() {
console.log(num);
// 问题:这时输出哪个num值?
}
}
// 输出 20
- 这时输出离变量最近的声明 (就近原则)
JavaScript预解析
- 情景一:
console.log(num);
var num = 10;
// 输出结果 undefined
- 情景二:
fn();
function fn() {
console.log('test');
}
// fn();
// 无论fn在定义函数前面还是定义函数后面,都可以正常输出 test
- 情景三:
fn(); // 报错
var fn = function() {
console.log('test');
}
// 这里只提升了var fn;
// js以为他是个变量,所以会报错
-
运行js代码时分为两步:1.预解析 2.代码执行
- 预解析:会把所有的 var 和 function 提升到作用域的最前面
- 代码执行:按照代码执行的顺序执行
-
注意!预解析只提升变量声明,不提升变量赋值
-
函数提升的时候把 function 声明的函数整个提升到最前面 (所以即使先使用后声明也可以正常运行)
预解析实例
var num = 10;
fun();
function fun() {
console.log(num);
var num = 20;
}
// 输出结果 undefined
- 解析:最外层function会提升到最前面,然后
fun()
调用这个函数的时候,函数预解析:
function fun() {
var num;
console.log(num);
num = 20;
}
-
所以输出的是undefined
-
特例!var a = b = c = 9 相当于 var a = 9; b = 9; c = 9;
-
所以这里的 b 和 c 直接赋值,相当于全局变量
JavaScript对象
- 对象是指一个具体的东西,而不是泛指
创建对象的三种方式 (自定义对象)
- 在对象里面声明的叫属性或方法
java里面的函数都是写在类内的,所以叫方法
直接创建对象
- 代码示例:
var obj = {}; // 创建一个空对象
var obj2 = { // 这里是直接新建了一个对象叫obj2
name : '菜鸟小铭', // 注意!这里是逗号
age : 18 ,
hello: function() {
console.log('Hello');
}
}
// 调用对象
console.log(obj2.name);
// 另一种方法
console.log(obj2['age']);
// 调用方法
obj2.hello();
- 注意!里面用的是冒号,多个属性用逗号隔开
- 方法后面跟的是匿名函数
利用new object创建对象
- 代码示例:
var obj = new Object(); // 创建一个空对象
// 然后追加属性和方法
obj.name = '代码小铭';
obj.age = 18;
obj.Hello = function() {
console.log('Hello');
}
// 调用和上面的一样
console.log(obj.name);
console.log(obj['age']);
obj.hello();
- 注意!Object必须要大写
利用构造函数创建对象
- 最接近其他语言的方法 (类似于类)
- 代码示例:
function Student(name) {
this.name = name;
this.study = function(active) {
console.log(active));
}
}
// 声明 声明时需要加new!
var xiaoming = new Student('菜鸟小铭');
// 调用对象
console.log(xiaoming.name);
xiaoming.study('study');
- 注意!构造函数首字母大写
- 构造函数不需要return就可以返回一个对象
- 属性和方法前面必须加this
new的作用
- new之后创建了一个空的对象
- 里面的this指针指向空对象
- 然后执行属性和方法
- 然后返回这个对象
遍历对象属性 (for…in循环)
- for in 循环可以用于数组和对象
- 代码示例:
var obj = {
name : '代码小铭',
age : 18,
gender : man,
}
for (var k in obj) {
console.log(k); // 得到的是属性名
console.log(obj[k]); // 得到的是属性值
}
- 小知识:for…in里面的变量喜欢用 k 或者 key
- 也可以遍历出方法
JavaScript内置对象
-
内置对象:js语言自带的对象
-
文档网站推荐:MDN网站
Math对象
- Math对象不是构造函数,所以不需要new,直接使用就可以
- 代码示例:
console.log(Math.PI);
// 输出3.14……
- 里面有max方法
- 参数不限个数
- 如果有不是数据的参数,则会返回NaN
- 如果没有参数返回-Infinity
绝对值和取整
- 代码示例:
// 绝对值
console.log(Math.abs(-1));
// 输出1
console.log(Math.abs('-1'));
// 同样输出1 (隐式转换)
// 向下取整
console.log(Math.floor(1.9));
// 输出1
// 向上取整
console.log(Math.cell(1.1));
// 输出2
// 四舍五入
console.log(Math.round(1.5));
// 输出2
// 四舍五入特例 (.5的时候往大了取)
console.log(Math.round(-1.5));
// 输出-1
随机数 (random)
- random 返回 0 到 1 之间的小数 (可以取到0,取不到1)
- 代码示例:
console.log(Math.random());
// 两个数之间的随机整数,包括这两个数
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min; //含最大值,含最小值
}
Date对象
- 时间开始于 1970 年 1 月 1 日
- Date是构造函数,需要 new 一个日期对象
- 代码示例:
var date = new Date();
// 如果没有参数,返回当前系统时间
console.log(date);
// 输出 Tue Mar 10 2020 20:43:20 GMT+0800 (中国标准时间)
// 数字型
var date1 = new Date(2019,10,01);
console.log(date1);
// 输出2019年11月1日 (比实际月份大 1 月)
// 因为月份是从0月开始算的
// 字符串型
var date2 = new Date('2019-10-1 20:03:20');
// 正常输出
日期格式化
- 推荐看MDN里的 get 开头的属性
- 以 dateFullYear,getMonth 为例
var date = new Date();
console.log(date.getFullYear());
// 输出2020
console.log(date.getMonth());
// 输出结果比实际少 1 月 (月份是从零开始算的)
- 注意:周日返回为 0 (可以使用数组来更改)
- 想要分钟等出现 02 这样,三元运算符判断,字符串连接
时间戳 (距离1970年的毫秒数)
- valueOf(),getTime(),+new Date(),Date.now()四种方法
- 应用:程序运行时间
- 代码示例:
var date =new Date();
console.log(date.valueOf());
console.log(date.getTime());
// 简单写法 (最常用)
var date1 = +new Date();
console.log(date1);
// H5新增方法
console.log(Date.noe());
时间戳示例:倒计时
- 不能直接用时分秒直接减(会出现负数),但是可以用时间戳可以直接减
- 代码实例:
function countDown(times) {
var nowTime = +new Date();
var inputTime = +new Date(time);
var times = inputTime - nowTime;
times /= 1000;
var dd = parseInt(times / 60 / 60 / 24);
var hh = parseInt(times / 60 / 60 % 24);
var mm = parseInt(times / 60 % 60);
var ss = parseInt(times % 60);
}
数组对象 (Array)
- new 新建数组详解:
var arr = new Array(2);
// 里面的2代表数组长度为2
var arr = new Array(2 , 3);
// 新建一个 [2,3] 的数组
检测是否为数组
- 用 instanceof 运算符来检测
- 代码示例:
var srr = [];
console.log(arr instanceof Array);
// 输出结果 true
console.log(Math instanceof Object);
// 输出结果 true
- instanceof详解
- 感觉是判断这个对象是否是这个构造函数的对象 (因为数组的类型其实也是对象)
- 用 Array.isArray(参数) 来判断 (H5新增方法)
- 代码示例:
var arr = [];
console.log(Array.isArray(arr));
// 输出结果 true
- 和 instanceof 的区别:
- isArray能检测到iframes
添加和删除数组元素
方法名 | 说明 | 返回值 |
---|---|---|
push(参数) | 在末尾添加一个或多个元素 | 返回新数组的长度 |
unshift(参数) | 在开头加入一个或多个元素 | 返回新数组的长度 |
pop() | 删除数组最后一个元素 | 返回删除元素的值 |
shift() | 删除数组第一个元素 | 返回删除元素的值 |
.splice(index,howmany,item) | 删除数组指定元素 | 返回删除元素的值 |
slice(begin,end) | 截取从begin到end | 返回新数组 |
-
详解 .splice 使用方法:
- index:删除哪一位的元素
- howmany:删除这位的元素多少个
- item:替换这个元素的值
-
代码示例:
var arr = new Array(1,2,3);
console.log(arr.push(4,'tom'));
// 输出5
console.log(arr);
// 输出[1,2,3,4,"tom"]
var arr = new Array(1,2,3);
console.log(arr.unshift(0));
// 输出4
console.log(arr);
// 输出[0,1,2,3]
var arr = new Array(1,2,3);
console.log(arr.pop());
// 输出3 (被删除的那个元素)
console.log(arr);
// 输出[1,2]
var arr = new Array(1,2,3);
console.log(arr.shift());
// 输出1 (被删除的那个元素)
console.log(arr);
// 输出[2,3]
var arr = [1,2,3,4,5]
arr.splice(2,2,0);
console.log(arr);
// 输出[1,2,0,0,5]
数组排序
方法名 | 说明 |
---|---|
reverse() | 颠倒数组中元素的顺序 |
sort() | 对数组元素进行排序 (冒泡排序) |
- 返回值是更新后的数组
- 代码示例:
var arr = [1,2,3];
arr.reverse();
console.log(arr);
// 输出[3,2,1]
var arr = [7,3,4,6,8,9,1];
arr.sort();
console.log(arr);
// 输出[1,3,4,6,7,8,9]
- sort排序是有缺陷的,他会先判断十位再判断个位
- 就会出现以下情况:
var arr = [13,4,45,7,77,1];
arr.sort();
console.log(arr);
// 输出 [1,13,4,45,7,77]
- 解决方法:
var arr = [13,4,45,7,77,1];
arr.sort(function(a,b) {
return a - b; // 升序排列
})
console.log(arr);
// 输出[1,4,7,13,45,77]
- 这里用a - b是升序排列,b - a就是降序排列
数组索引
方法名 | 说明 |
---|---|
indexOf() | 数组查找指定元素的第一个索引 |
lastIndexOf() | 数组查找指定元素最后一个索引 |
- 返回的是元素的下标
- 如果不存在则返回 -1
- 代码示例:
var arr = [1,2,3,4,5,2];
console.log(arr.indexOf(2));
// 返回1
console.log(arr.lastIndexOf(2));
// 返回5
数组索引实例:数组去重
- 我的解题思路:如果前面索引和后面索引的值相等,那么直接跳过;如果前面的索引和后面的索引不等,则去除后面索引到的元素,并且key-1 (为了防止出现多个重复)
- 代码示例:
var arr = ['c', 'a', 'z', 'a', 'x', 'a', 'x', 'a', 'c', 'n'];
for (var key = 0; key < arr.length; key++) {
if (arr.indexOf(arr[key]) == arr.lastIndexOf(arr[key])) {
continue;
}
if (arr[key] == arr[arr.lastIndexOf(arr[key])]) {
arr.splice(arr.lastIndexOf(arr[key]), 1);
key--;
}
}
console.log(arr);
- 老师的解法:遍历旧数组,然后查询新数组是否有重复,如果没有重复则添加
var arr = ['c','a','z','a','x','a','x','a','c','n'];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
if (newArr.indexOf(arr[i]) == -1) {
newArr.push(arr[i]);
}
}
console.log(newArr);
老师的方法比我的方法简单一万倍
数组转化为字符串
方法名 | 说明 |
---|---|
toString() | 数组转化为字符串,逗号分隔 |
join(‘分隔符’) | 分隔符分割开 (什么都不写默认是逗号) |
- 返回值是转换好的字符串
- 代码示例:
var arr = [1,2,3];
console.log(arr.toString());
// 输出1,2,3
console.log(arr.join(''));
// 输出123
字符串对象
- 字符串是简单数据类型,但是有属性和方法
- 基本包装类型:把简单数据类型包装成复杂数据类型 (String, Number, Boolean)
- 过程:
var str = '菜鸟小铭';
var temp = new String('菜鸟小铭');
str = temp;
temp = null; // 销毁中间变量
字符串的不可变
-
用户声明一个字符串并且赋初值后,再拿另一个值去覆盖这个变量,初始的赋值并没有被销毁,而只是变量名指向了另一个值 (地址改变)
-
不可变的体现:
var str = '';
for (var i = 0; i < 10000000; i++) {
str += i;
}
console.log(str);
- 根据chrome的任务管理器来看,循环一千万次需要内存629.34MB内存 (如果电脑内存不是特别大不建议尝试)
- 所以不要随意拼接字符串
在字符串里面查找字符
方法名 | 说明 |
---|---|
indexOf(‘要查找的字符’,开始的位置) | 从前往后找,只找第一个 |
lastIndexOf(‘要查找的字符’,开始的位置) | 从后往前找,只找第一个匹配的 |
- 返回在字符串里的位置,找不到返回 -1
- 代码示例:
var str = 'JavaScript!';
console.log(str.indexOf('S'));
// 返回4
var str = 'assa';
console.log(str.indexOf('a',1));
// 返回3
// lastIndexOf同理
根据位置返回字符
方法名 | 说明 |
---|---|
charAt(index) | 返回指定位置的字符 |
charCodeAt(index) | 获取指定位置字符的ASSII码 |
str[index] | 获取指定位置字符 (H5新增) |
- 代码示例:
var str = 'JavaScript!';
console.log(charAt(5));
// 输出c
console.log(charCodeAt(5));
// 输出99
cosnle.log(str[5]);
// 输出c
- 可以使用对象名[‘属性’]的方法来获取对象里面的属性及属性的值
字符串的操作
方法名 | 说明 |
---|---|
concat(str1,str2) | 连接 数组/字符串,等效于 + |
substr(start,length) | 截取字符串 从 start 处开始截取,截取 length 这么长 重要 |
replace(‘被替换的字符’,‘替换为的字符’) | 替换字符 (只替换第一个) |
split(‘分隔符’) | 字符转换为数组 (和join类似) |
- 代码示例:
var str = 'abca';
console.log(str.replace('a','z'));
// 输出 zbca
var str = 'a,b,c';
console.log(str.split(','))
// 输出 [a,b,c]
JavaScript数据类型
简单数据类型
-
包括:Number,String,Bollean,undefined,null
-
null 是一个对象
- 应用:有个变量以后打算存对象,可以用 null占位
-
在栈中储存数据
-
简单数据类型传参
- 直接把栈里面的值覆盖掉
复杂数据类型
-
用 new 创建的类型
-
包括Object,Array
-
在堆中储存:在栈中存放地址,地址指向堆,在堆里面存放数据
-
复杂数据类型传参
- 先找到放在栈中的变量名,取出里面的地址,找到地址指向的堆,再修改里面的变量