JavaScript基础(三)
函数
概述
函数:就是封装了一段可被重复调用执行的代码块。通过此代码块可以实现大量代码的重复使用。 封装:把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口。
函数的声明与调用
// 声明
function funcName(params) {
// function statements
}
funcName(params);
// 调用
形参与实参
声明时传入的为形参,调用时传入的为实参。
实参个数与形参个数不匹配的情况
参数个数 | 说明 |
---|---|
形参和实参个数相等 | 输出正确结果 |
实参个数多于形参 | 只取到形参的个数 |
实参个数少于形参 | 多的形参定义为undefined,结果为NaN |
function sum(num1, num2) {
console.log(num1 + num2);
}
sum(100, 200); // 300, 形参和实参个数相等,输出正确结果
sum(100, 400, 500, 700); // 500, 实参个数多于形参,只取到形参的个数
sum(200); // NaN, 实参个数少于形参,多的形参定义为undefined,结果为NaN
在JavaScript中,形参的默认值是
undefined
。
声明函数的三种方法
function命令
function funcName(params) {
// function statements
}
函数表达式
const funcName = function(params) {
// function statements
}
箭头函数=>
创建一个函数更加简洁的方式,有两种方式:
- 不带花括号:
(...args) => expression
,计算表达式,直接返回。 - 带花括号:
(...args) => { bodu }
,可以编写多行多个语句,需要return
语句返回。
let sum = (a, b) => a + b;
Function构造函数
const add = new Function(
'x',
'y',
return 'x + y'
);
注意
- 函数未指定返回值则默认返回
undefined
arguments的使用
arguments
是所有JS函数内置的对象,但也只有函数具有。
function test() {
return arguments;
}
console.log(test(1,2,3,4));
输出:
Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]
函数的 arguments
是一种伪数组:
- 具有数组的
length
属性 - 按照索引方式进行存储
- 没有真正数组的一些方法
pop()
、push()
数组
基本概念
- JS 数组都是动态创建的,可以自由增加数组长度,这点不同于 C/C++。
- 一个
JS
数组内可以存放不同类型的元素,例如['abc', 1, true, undefined]
,这点也不同于 C/C++/Java。
创建数组
数组字面量创建数组
-
创建空数组
let arr = [];
-
创建一般数组
let arr = [1, 2, 3];
new Array 创建数组对象
-
创建空数组
let arr = new Array();
-
创建指定长度的数组,有2个空数组元素
let arr1 = new Array(2);
-
创建放有指定元素的数组([2, 3])
let arr2 = new Array(2, 3);
-
一些示例
let arr_0 = [1, true, "aaa"]; let arr_1 = []; let arr_2 = new Array(); // [] let arr_3 = new Array(2); // [empty × 2] let arr_4 = new Array(2,); // [empty × 2] let arr_5 = new Array(2, 3, 4); // [2, 3, 4]
基本操作
-
数组元素访问
let e = arr[index];
-
获取数组长度
let len = arr.length;
-
数组遍历
方式一:for (let i = 0; i < arr.length; i++) { console.log(arr[i]); }
方式二:
for (const i in arr) { console.log(arr[i]); }
-
数组逆转
// 反转数组 function reverse(arr) { let res = []; for (let i = arr.length - 1; i >= 0; i--) { res[res.length] = arr[i]; } return res; } console.log(reverse([1, 2, 3, 4, 0])); // [0, 4, 3, 2,
检测一个值是否为数组
instanceof
function isArray(test) {
if (test instanceof Array) return true;
return false;
}
console.log(isArray([1, 2])); // true
console.log(isArray(1)); // false
Array.isArray[]
Array.isArray()
方法用于检测一个值是否为数组。
添加删除数组元素的方法
方法名 | 说明 | 返回值 |
---|---|---|
push(arg1,...) | 末尾添加一个或多个元素 | 返回新的长度 |
pop() | 删除数组最后一个元素,数组长度减 1,无参数,修改了原数组 | 返回所删除元素的值 |
unshift() | 向数组的开头添加一个或多个元素,修改了原数组 | 返回新的长度 |
shift() | 删除数组的第一个元素,数组长度减 1,无参数,修改了原数组 | 返回第一个元素的值 |
数组排序
方法名 | 说明 | 是否修改原数组 |
---|---|---|
reverse() | 颠倒数组中元素顺序,无参数 | 会改变原数组,返回新数组 |
sort() | 对数组的元素进行排序 | 会改变原数组,返回新数组 |
sort
方法对数组进行原地排序,但是默认按照字典序排序。需要传入一个比较函数 cmp(a, b)
,然后得到我们需要的排序效果。
let arr = [1, 4, 17, 12, 9];
arr.sort();
console.log(arr); // [ 1, 12, 17, 4, 9 ]
let cmp = (a, b) => a - b;
arr.sort(cmp);
console.log(arr); // [ 1, 4, 9, 12, 17 ]Copy to clipboardErrorCopied
其中,let cmp = (a, b) => a - b;
为升序,b - a
为降序。
数组索引方法
方法名 | 说明 | 返回值 |
---|---|---|
indexOf() | 数组中查找指定元素的 第一个索引 | 若存在则返回索引号,否则返回 -1 |
latsIndexOf() | 查找指定元素在数组中的 最后一个的索引 | 若存在则返回索引号,否则返回 -1 |
indexOf
前面开始查找,lastIndexOf
从后面开始查找,但索引都是从前往后由 0
算起。
let arr = ['red', 'green', 'blue', 'pink', 'blue'];
console.log(arr.indexOf('blue')); // 2
console.log(arr.lastIndexOf('blue')); // 4
console.log(arr.indexOf('yellow')); // -1
数组去重
// 数组去重
function unique(arr) {
let res = [];
for (let i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) {
res.push(arr[i]);
}
}
return res;
}
console.log(unique([ 2, 3, 3, 4, 5, 5 ])); // [ 2, 3, 4, 5 ]
数组与字符串互转
数组转换为字符串
方法名 | 说明 | 返回值 | 是否改变原数组 |
---|---|---|---|
toString() | 将数组转换成字符串,逗号分隔每一个 | 返回一个字符串 | 不改变 |
join('分隔符') | 把数组中的所有元素转换为一个字符串,以指定符号分割 | 返回一个字符串 | 不改变 |
示例
let a = ['a', 'b', 'c', 'd', 'e'];
console.log(a.toString()); // a,b,c,d,e
console.log(a.join('')); // abcde
console.log(a.join('-')); // a-b-c-d-e
console.log(a); // [ 'a', 'b', 'c', 'd', 'e' ]
字符串转换为数组
使用 split()
方法。
let str = 'blue-green-pink-red';
let res = str.split('-');
console.log(res); // [ 'blue', 'green', 'pink', 'red' ]
作用域
作用域概述
通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。作用域的使用提高 程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。
全局变量
- 在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。
- 全局变量在代码的任何位置都可以使用
- 在全局作用域下var声明的变量是全局变量。
- 特殊情况下,在函数内不使用var声明的变量也是全局变量(不建议使用)
局部变量
在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
- 局部变量只能在该函数内部使用
- 在函数内部var声明的变量是局部变量
- 函数的形参实际上就是局部变量
全局变量与局部变量区别
- 全局变量:在任何一个地方都可以使用,只有在浏览器关闭时才会被销毁,因此比较占内存
- 局部变量:只在函数内部使用,当其所在的代码块被执行时,会被初始化;当代码块运行结束后,就会被销毁,因此更节省内存空间
var、let、const
ES6
以前,JS
没有块级作用域。ES6
新增 let
和 const
之后才有了块级作用域。 块级作用域是指用 {}
包括起来的一段代码,例如 if 、while 等等。 函数作用域就是指变量只在函数内部起作用。
var
声明的是函数作用域的变量let
声明的是块级作用域的变量const
声明的是块级作用域的变量
作用域链
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链
声明提升
- 我们
js
引擎运行js
分为两步: 预解析,代码执行- 预解析
js
引擎会把js
里面所有的var 还有function提升到当前作用域的最前面 - 代码执行 按照代码书写的顺序从上往下执行
- 预解析
- 预解析分为变量预解析(变量提升) 和函数预解析(函数提升)
- 变量提升就是把所有的变量声明提升到当前的作用域最前面 不提升赋值操作
- 函数提升就是把所有的函数声明提升到当前的作用域最前面,不调用操作
举例一
func();
var func = function () {
console.log('hello');
}
// 出错,以上代码相当于:
var func;
func();
func = function () {
console.log('hello');
}
举例二
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;
c = 9;
b = c;
a = b;
console.log(a);
console.log(b);
console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
输出
// answer
/*
9
9
9
9
9
error
*/
PREVIOUS
创建对象
对象
什么是对象?
在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的。
- 属性:事物的特征,在对象中用属性来表示(常用名词)
- 方法:事物的行为,在对象中用方法来表示(常用动词)
为什么需要对象?
创建对象
- 使用字面量创建对象
- 使用
new Object
创建对象 - 利用构造函数创建对象
字面量创建
使用 {}
创建,包含属性和方法,采用键值对表示,创建的对象称为对象字面量。
var obj = {
uname: 'MurphyChen',
age: 18,
sayHi: function () {
console.log('Hi!);
}
}
使用对象的属性和方法
- 调用对象的属性
// 方法一
objectName.attrName
// 方法二
objectName['attrName'] // 不要忘记引号
- 调用对象的方法
objectName.funcName();//不要忘记括号
使用newObject创建对象
// 创建空对象
let obj = new Object();
//添加属性
obj.uname = 'MurphyChen';
obj.age = 18;
obj.sayHi = function() {
console.log('Hi!');
}
利用构造函数创建对象
前两种创建对象的方法,每次都只能创建一个对象。但需要多个具有相同属性和方法的对象的时候,就需要使用构造函数来创建。
构造函数将相同的属性和方法封装在一个函数里。
构造函数语法
// 定义
function ConFuncName(params) {
this.attr = value;
this.methods = function() {};
}
// 调用
let obj = new ConFuncName(params);
- 构造函数名单词首字母均大写
- 函数不需要返回值
举例
function Star(uname, age, sex) {
this.name = uname;
this.age = age;
this.sex = sex;
}
let ldh = new Star('刘德华', 18, '男');
let zxy = new Star('张学友', 19, '男');
console.log(typeof ldh); // object
console.log(ldh.sex); // 男
console.log(zxy.name); // 张学友
构造函数的实质
构造函数相当于创建了一个抽象的类,使用关键字 new
创建一个对象的过程称为类的实例化,对象是具体的。
new关键字的执行过程
- 在内存中创建一个空的对象;
this
指向这个空对象;- 执行构造函数里面的代码,给空对象添加属性和方法;
- 返回此对象。
遍历对象
for...in
可以对数组和对象进行遍历。
语法
for (const key in obj) {
console.log(key); // 遍历属性名
console.log(obj[key]); // 遍历属性值
}