上一篇:16.JS基础(二)
JavaScript学习
函数
- 通俗讲:函数就是可重复执行的代码块
- 函数的作用:
- 通过函数可以封装任意多条语句,以便在需要的时候随时调用
- 将代码编写在函数中,就可以避免在非必要情况下调用该代码
- 函数会形成一个相对封闭的空间(局部作用域)
函数的编写及调用
- JavaScript中的函数使用function关键字来声明,后跟一组参数以及函数体
function 函数名(参数) {
//函数体
// do something
}
- 函数需要调用才会执行:函数名(参数);
function test(){
console.log('hello')
}
test() //调用执行函数
内置函数:
- Number()、String()、alert()、parseInt()、prompt()…
自定义函数:
- 函数声明、函数表达式、匿名函数
注意:
- 函数声明(任意位置调用)
- 函数表达式(先定义后调用)
- 匿名函数(不允许单独定义)
函数的参数
- 参数的意义是让程序更加灵活
- 形参:形式参数,函数定义时的参数,函数内部的变量
- 实参:实际参数,函数调用时的参数,是具体值,实参的值一一对应传递给形参
形参和实参的个数可以不一致
- 形参可以多于实参
- 实参可以多于形成
- 函数内部可以通过 arguments对象(实参副本对象),访问到所有的实参
length属性:
- 函数的length属性,返回函数的形参个数
函数的返回值
- 所有函数都有返回值
- 如果函数没有明确的返回值,默认返回 undefined
- 使用 return 语句可以自定义函数的返回值
- return 语句会终止当前函数的执行并返回函数的值
- 注意,函数内 return 语句之后的所有代码都不会执行!
作用域
- 在程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域
- 通俗讲,作用域就是某个名字起作用的范围
- 在 JavaScript 中, 作用域为可访问变量(包含对象和函数)的集合。
- 作用域有
全局作用域
和局部作用域
全局作用域
- 在整个页面起作用,在
<script>
内都能访问到 - 在全局作用域中有个全局对象window,代表一个浏览器窗口,由浏览器创建,可以直接调用
- 全局作用域中声明的变量和函数,会作为window对象的属性和方法保存
- 变量在函数外声明,即为全局变量,拥有全局作用域
var a = 123;//全局变量
function fn() {//全局函数
console.log(a);//123
}
fn();
console.log(a);//123
局部作用域
在 JavaScrip 中,函数是唯一拥有自身作用域的代码块
- 局部作用域内的变量只能在函数内部使用,所以也叫函数作用域
- 变量在函数内声明,即为局部变量,拥有局部作用域
function fn() {
var b = 456;//局部变量
console.log(b);//456
}
fn();
console.log(b);//b is not defined
注意:
- 可以直接给一个未声明的变量赋值,但不能直接使用未声明的变量
- 因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量
- 当全局与局部有同名变量的时候,访问该变量将遵循
就近原则
变量的生命周期
- 全局变量在页面打开时创建,在页面关闭后销毁
- 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁
声明提升机制
- 在 JavaScrip 中变量声明和函数声明,
声明
会被提升到当前作用域的顶部
var a = 1;
function test() {
alert(a);
var a = 2;
alert(a);
}
test();
alert(typeof fn);
var fn = 10;
function fn() {}
alert(typeof fn);
JS解析器
- 浏览器中有一套专门解析JS代码的程序,这个程序称为js的解析器
- 浏览器运行整个页面文档时,遇到
<script>
标签时JS解析器开始解析JS代码 - JS解析器的工作步骤:
-
预解析代码
- 主要找一些关键字如var、function、参数等,并存储起来(内存中)
- 变量、参数的初始值为 undefined
- 函数的初始值就是该函数的代码块
- 当变量和函数重名时,不管顺序谁前谁后,只留下函数的值(函数优先)
- 当函数和函数重名时,会留下后面那个函数(覆盖)
-
逐行执行代码
- 当预解析完成之后,就开始逐行执行代码,内存中变量的值随时都可能会发生变化
示例解读:见代码(解析器.html)
自执行函数
要执行一个函数,我们必须要有方法定位函数、调用函数
匿名函数如何调用?(匿名函数不允许单独定义)
匿名自执行函数,也叫立即执行函数(IIFE)
(function () {
console.log(123)
})()
自执行函数的好处:独立的作用域,不会污染全局环境!
匿名函数传参:
(function (a,b) {
console.log(a + b)
})(2,3)
常见形式:
(function () {
console.log(11)
})()
(function(){
console.log(22)
}())
!function() {
console.log(33)
}()
+function() {
console.log(55)
}()
-function() {
console.log(66)
}()
......
回调函数
- 把函数作为参数传递给另一个函数,我们把该函数(作为参数的函数)称为回调函数
- 通常当特定的事件或条件发生的时候,我们调用回调函数对事件进行处理
function fn1(callback){
var a = 123
console.log('函数fn1执行')
callback(a)// 回调函数
}
function fn2(num){
console.log('函数fn2执行,传入参数:' + num)
}
fn1(fn2)
递归函数
案例:老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?
function findAge(numberOfChildren, eldestAge, ageDifference) {
if (numberOfChildren === 1) {
return eldestAge;
} else {
return findAge(numberOfChildren - 1, eldestAge - ageDifference, ageDifference);
}
}
console.log(findAge(4, 16, 2)); // 10
案例:公园里有一堆桃子,猴子每天吃掉一半,挑出一个坏的扔掉,第6天的时候发现还剩1个桃子,问原来有多少个桃子?
/** 我们可以这样思考:
在第6天,我们知道只剩下一个桃子,那么在第5天的时候,这个桃子在被猴子吃掉一半并且扔掉一个坏的之后,就剩下了一半的桃子。
我们可以用这种方式继续往前推,直到第一天
*/
function findOriginalPeaches(day, remaining) {
if (day === 1) {
return remaining;
} else {
return findOriginalPeaches(day - 1, (remaining + 1) * 2);
}
}
console.log(findOriginalPeaches(6, 1)); // 63
阿里巴巴2015年前端面试题:
/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从1开始 fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …] >getNthFibonacci(3) 返回值为2
getNthFibonacci(6) 返回值为8
*/
/**
斐波那契数列是一个无限序列,在该序列中,前两个数字为1,从第三个数字开始,每个数字都是前两个数字的和。
因此,我们可以使用自顶向下的递归解决方案来找出给定位置的斐波那契数。
注意,因为斐波那契序列起始两个数都为1,我们需要对n=1和n=2的情况进行特殊处理。
*/
function getNthFibonacci(n) {
if (n <= 0) {
throw new Error("Input should be a positive integer.");
} else if (n === 1 || n === 2) {
return 1;
} else {
return getNthFibonacci(n - 1) + getNthFibonacci(n - 2);
}
}
console.log(getNthFibonacci(3)); // 2
console.log(getNthFibonacci(6)); // 8
- 如果一个函数内部调用函数自身,这个函数就是递归函数
- 递归函数的本质:函数循环调用
- 一般来说,递归需要有边界条件、递归前进段和递归返回段
- 当边界条件不满足时,递归前进;当边界条件满足时,递归返回
// 老王有四个子女,老四比老三小2岁,老三比老二小2岁,老二比老大小2岁,老大现在16岁,问老四几岁?
function countAge(who) {
if (who == 1) {
return 16;
} else {
return countAge(who - 1) - 2;
}
}
alert(countAge(4)); // 10
递归函数在运行的时候,每调用一次函数就会在内存中开辟一块空间,内存消耗较大,注意防止栈溢出。
阿里巴巴2015年前端面试题
/**
*@desc: fibonacci
*@param: count {Number}
*@return: result {Number} 第count个fibonacci值,计数从1开始
fibonacci数列为:[1, 1, 2, 3, 5, 8, 13, 21, 34 …]
getNthFibonacci(3) 返回值为2
getNthFibonacci(6) 返回值为8
*/
// 斐波那契数列的定义者,是意大利数学家列昂纳多·斐波那契
// 斐波那契数列:[1, 1, 2, 3, 5, 8, 13, 21, 34...]
// 数列从第3项开始,每一项都等于前两项之和
function getNthFibonacci(count){
if (n <= 2) {
return 1;
}
return getNthFibonacci(n-2) + getNthFibonacci(n-1);
}
console.log( getNthFibonacci(6) );//8
console.log( getNthFibonacci(9) );//34
递归算法一般用于解决三类问题:
- 数据的定义是按递归定义的
- 问题解法按递归算法实现
- 数据的结构形式是按递归定义的
构造函数(了解)
-
构造函数:用于创建特定类型的对象
-
JS内部构造函数:Object、Number、String、Array、Function、Boolean等等…
-
当任意一个普通函数用于创建一类对象,并通过new操作符来调用时它就是构造函数
-
构造函数一般首字母大写
对象 Object
- 对象是一组无序的键值对,是带有属性和方法的集合
- 通俗讲,对象就是无序的数据集合
- 属性是与对象相关的值,方法是能够在对象上执行的动作
- 对象的作用:用于在单个变量中存储多个值
创建对象
- 对象成员由 key键:value值 组成
- 键:为字符串,一般用引号引起来(不用引号也可以)
- 值:可以是任意类型的数据
var obj1 = {} // 直接赋值{}
obj1.name = '小错'
obj1.age = 18
obj1.sayHi = function (){
alert('hi,大家好')
}
var obj2 = new Object() // 构造函数创建
obj2.name = '小错'
obj2.age = 18
obj2.sayHi = function (){
alert('hi,大家好')
}
var obj3 = { // 对象字面量
name: '小错',
age: 18,
sayHi: function (){
alert('hi,大家好')
}
}
访问对象成员
- 使用点号访问对象成员,如:对象.属性 对象.方法()
- 使用中括号访问对象成员,如:对象[变量或字符串]
删除属性
delete 对象.属性
遍历对象
// for/in 循环
for (var key in obj){
console.log( obj[key] );
}
数组 Array
- 数组,是有序的元素序列
- 通俗讲,数组就是有序的数据集合
- 数组属于对象类型
- 数组的作用:用于在单个变量中存储多个值
创建数组
var arr1 = [ ];
var arr2 = new Array( );
var arr3 = new Array( size );
var arr4 = new Array( el1, el2, el3 ... );
基本操作
var arr5 = ['a', 'b', 'c']
- 访问数组元素:arr5[0]、arr5[1]、arr5[2]
- 中括号里的数字为数组的索引(下标),数组的索引总是从0开始!
- 设置数组元素的值:arr5[1] = 2; arr5[4] = 5;
- 数组中的元素值可以是任意数据类型
- 获取数组的长度(数组中元素的个数):arr5.length(可读写)
清空数组
- arr = [];
- arr.length = 0; //推荐使用
遍历数组
-
for 循环
var arr1 = ['a','b','c','d'] arr1.abc = '呵呵' console.log( arr1 ) console.log( arr1.abc ) for (var i = 0; i < arr1.length; i++){ console.log( arr1[i] ) } console.log( '--------------------------------------' )
-
for/in 循环
var arr2 = ['a','b','c','d'] arr2.abc = '呵呵' // 动态添加属性 // console.log( arr2 ) // console.log( arr1['abc'] ) for (var index in arr2){ console.log( index )// '0' '1' '2' '3' 'abc' // console.log( arr2[index] )// a b c d 呵呵 }
-
- 自动取出下标,下标为字符串
- 有值的索引位置才会被遍历出来
- for/in一般用于遍历对象,不推荐用于遍历数组
数组的常用方法
- push() 在数组的后面添加(单个或多个)元素,返回数组新的长度
- pop() 删除数组的最后一个元素,返回被删除的元素
- unshift() 在数组的前面添加(单个或多个)元素,返回数组新的长度
- shift() 删除数组的第一个元素,返回被删除的元素
- splice() 可以对数组进行增、删、改,返回一个数组(被删除的元素)
- 删除n项:arr.splice(起始位置,n)
- 增加元素:arr.splice(起始位置,0,添加1,添加2…)
- 修改元素:arr.splice(起始位置,2,替换1,替换2)
- reverse() 将数组进行倒转,返回数组
- arr.reverse();
- sort() 将数组进行排序,返回数组,详情看下方
数组排序
以上方法都会改变原数组
- slice() 从数组中拷贝一部分,返回新数组
- arr.slice(1,4) 包含索引为1的元素,不包含索引为4的元素
- concat() 用于合并数组,返回新数组
- arr.concat(arr1,arr2…)
- toString() 将数组转成字符串,返回字符串
- join() 将数组转成字符串,参数为分隔符,返回字符串
以上方法都不会改变原数组
对象数组
var arr1 = [
{name: '小明', age: 17},
{name: '小红', age: 18},
{name: '小东', age: 19}
]
多维数组
var arr2 = [
[1,2,3],
[4,5,6],
[7,8,9]
]
数组排序
- sort( [fn] ) 排序,返回数组
- arr.sort(); //默认按照字符编码排序,先比较第一位
arr.sort(function (a,b) { //升序,只能对数值排序
return a-b;
});
arr.sort(function (a,b) { //降序,只能对数值排序
return b-a;
});
arr.sort(function (a,b) {//升序,自定义排序函数
if (a > b) {
return 1;
}
if (a < b) {
return -1;
}
return 0;
});
思考:如何对如下数组按年龄升序排列
var arr = [{name:'xm',age:23},{name:'xh',age:18},{name:'xf',age:20}``];
arr.sort(function (a,b){
return a.age - b.age
})
console.log(arr)
选择排序
- 假设一个最小值以及最小值的下标
- 找出最小值以及最小值的下标
- 假设的最小值与找出的最小值互换位置
var arr = [23,46,13,87,43,12,31,211,55,667]
for (var n = 0; n < arr.length-1; n++){
// 1.假设一个最小值以及最小值的下标:
var min = arr[n];
var minIndex = n;
// 2.找出最小值以及最小值的下标:
for (var i = n+1; i < arr.length; i++){
if(min > arr[i]){
min = arr[i];
minIndex = i;
}
}
// 3.假设的最小值与找出的最小值互换位置:
var tmp = arr[n]; // 中间变量
arr[n] = min;
arr[minIndex] = tmp;
}
console.log(arr);
冒泡排序
- 相邻两个数进行比较,互换位置…
for (var n = 0; n < arr.length-1; n++){ //比较轮数
for (var i = 0; i < arr.length-(n+1); i++){ //每一轮比较次数
if (arr[i] > arr[i+1]) {//前面的大于后面的:换位 升序
var tmp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = tmp;
}
}
}
console.log(arr)
快速排序
- 找中点,分左右,递归运算…
function quickSort(arr){
// 递归出口
if (arr.length <= 1) return arr;
// 找中点(中点的下标及值)
var midIndex = parseInt( arr.length/2 );
var mid = arr[midIndex];
// 分左右
var left = [];
var right = [];
for (var i = 0; i < arr.length; i++){
if (arr[i] === mid) {
continue; //跳过本次循环
}
if (arr[i] < mid) {//与中点比较分左右
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归运算(左中右三个数组合并)
return quickSort(left).concat([mid],quickSort(right));
}
下一篇:18.JS基础(四)
🌸友情推荐:全栈大佬笔记 Android领域肥宅