0 介绍
0.1 ECMAScript
ECMA-262 的第 5 版是 JS 的第一个稳定版本,得到了各浏览器厂商的支持。<br />
- 语法
- 类型
- 语句
- 关键词
- 保留字
- 操作符
- 对象
0.2 DOM
文档对象模型是针对 XML 但经过扩展用于 HTML 的 API 。DOM 把整个页面映射为一个多层次节点结构。HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。<br />document object model 文档对象模型,里面提供了一些属性和方法,可以让我们操作页面的元素
0.3 BOM
原则上讲,BOM只处理浏览器窗口和框架,但下面一些针对浏览器的 JS 扩展也被看做是BOM的一部分。<br />browser object model 浏览器对象模型,里面提供了一些属性和方法,可以让我们操作浏览器。
1 导入JS
1.1 三种常见导入
1.1.1 行内导入JS(慎重:不安全)
<div onclick="alert('hello world')"></div>
1.1.2 内嵌式
<script>
alert('hello world')
</script>
1.1.3 外链式
// 新建一个js文件
<script src="./js/demo.js"></script>
// or
<script src="./js/demo.js" type="text/javascript"></script>
1.2 内嵌与外链不能同时操作
内嵌导入和外链导入不能合并在一起,如果当前是外链导入的,那么在script脚本块找那个编写的所有代码都不会被执行。
<script src="./js/demo.js">
alert('hello world')
</script>
1.3 位置编写位置
我们一般性会把css放在body的上面,把js放在body末尾(约定速成的规范)
但是如果放在了标签前面那么将如何处理?
页面加载完所有资源之后执行操作
在js中
window.onload=function(){
}
在jq中
$(document).ready(function(){
})
window.addEventListener('load',function(){},false);
// ie8以下
window.attachEvent('onreadystatechange',function(){
})
2 JS常用的输出方式
2.1.弹窗输出
2.1.1 alert
- 在浏览器中弹出一个提示框(提供确定按钮,点击确定弹窗消失)
- 使用alert弹窗信息,提示的内容最后都会转换成字符串(调用了toSring这个方法)
alert(1)
alert({name:'wjw'}) //=> '[object Object]'
alert([13,14]) //=> '12,13'
2.1.2 confirm
- 在alert基础上增加了让用户选择的操作(提供两个按钮:确定和取消)
- 当用户点击确定按钮的时候,我们接收到的结果是true,点击是取消按钮我们接受到的结果是false,此后我们可以根据结果来处理即可
var wjw = confirm("are you sure");
alert(wjw);
2.1.3 prompt
- 在confirm 基础上增加让用户增加输入的效果
- 用户点击取消按钮,我们获取到的结果是null,如果用户点击是确定按钮,我们将获取用户输入的信息
- 在真实的项目中,一般性会使用二次封装的弹窗,不会使用这种弹窗
var flag = prompt("are you sure");
alert(flag)
2.2 控制台输出
控制台输出,方便开发调试
2.2.1 console
- 在控制台输出,优势不会转换数据类型,输出什么格式的数据都可以
console.log({name:'wjw'});
console.dir() //比log输出更加详细一些
console.table //把json数据展示成一个表格
3 JS定义值
语法
ECMAScript 的语法大量借鉴了 C 及其他类 C 语言(如 Perl 和 Java)的语法。
区分大小写
3.1 注释
// 单行注释
/*
* 这是一个多行
* (块级)注释
*/
3.2 严格模式
严格模式
ES5 引入了严格模式的概念,在严格模式下,ES3 中的一些不确定行为将得到处理,而且队某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:<br />这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指
示(pragma),用于告诉支持的JavaScript引擎切换到严格模式。在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:
function doSomething(){
"use strict"; //函数体
}
3.3 变量、常量
变量是可以变得
常量是不可变的
3.3.1变量
- 变量其实只是一个无意义的名字,它所代表的意义都是其存储的那个值
- 让原有的num变量存储的值修改为13(变量只能代表一值)
js定义变量的方法
// var 变量名 = 值;
var num = 12;
var name = 'wjw'
3.3.1常量
- 任何一个具体的数据都是常量,例如12是个常量
- 和变量累死,我们设置一个常量(也就是一个名字),给其存储一个值,但是这个存储的值不能修改
const num = 12;
3.4 JS中命名规范
- JS中严格区分大小写
var test = 'wjw';
var Test = 'wjh';
console.log(test);
// 输出test
- 遵循国际命名规则"驼峰命名法"
第一个单词首字母小写,其余每一个有意义单词首字母大写
var studentInfo; // 学生信息
// 所见即所得
// 例子:
/*
* info : information 信息
* init : initlization 初始化
* add/insert/create 增加插入创建
* remove/rm/clear/del/delete 删除
* update 修改
* get/query/select : 查询获取
*/
- 命名的时候可以使用$、_、数字、字母,但是数字不能作为名字的第一位
var student_info;
var $xxx; //=> 一般都是应用jq获得到的值
var _xxx; //=> 一般这样的情况代表变量是一个局或者公共的变量
- JS中很多字都是有特殊含义的,我们这些词,叫做关键词;现在没有特殊含义,以后可能会作为关键的,我们叫做保留字;二关键词和保留字不可以随便用来命名;
<br />
4 JS数据类型
4.1 数据类型
4.1.1 基本数据类型(值类型)
- Number 数字
- String 字符串
- 单引号包裹起来的都是字符串(单双号没有区别)
- Boolean 布尔
- true false => 布尔值只有两个值
- null 空对象指针
- undefined 未定义
4.1.2 引用数据类型
- {} 普通对象
- [] 数组
- /^$/ 正则
- ...
4.1.3 function数据类型
- funciotn fn (){}
4.2 数据类型检查
- typeof
- 用来检测数据类型的运算符
- instanceod
- 检测某个实例是否属于这个类
- constructor
- 获取当前实例的构造器
- Object prototype.toSting.call()
- 获取当前实例的所属类信息
4.2.1 typeof
操作符
typeof 是用来检测给定变量的数据类型的操作符。对一个值使用 typeof 操作符可能返回下列某个字符串:
"undefined"
"boolean"
"string"
"number"
"object" // 如果这个值是对象或者null "function"
4.3 布尔值
Boolean()
- 把其他数据类型的值转化为布尔类型
- 只有0、Nan、null、undefined这五个数据值转换为布尔类型的false,其余的都会变成true
叹号在JS中海油一个作用:取反,先把值转换为布尔类型,然后再去取反
!!
在叹号取反的基础上取反,取反两次相当于没有操作,但是却已经其他类型值转化为布尔类型了,和Boolean是相同的效果
4.4 字符串
在JS中单引号和双引号包起来的都是字符串
12 - > number
'12' -> string
'[12,23]' -> string
4.4.1 toString()
第一种是使用几乎每个值都有的 toString()方法。多数情况下,调用 toString() 方法不必传递参数,但在调用数值的 toString()方法时,可以传递一个参数:输出数值的基数。默认情况下,toString() 方法以十进制格式返回数值的字符串表示。而通过传递基数,toString() 可以输出二进制、八进制、十六进制等。
var num = 10;
alert(num.toString()); // "10"
alert(num.toString(2)); // "1010"
alert(num.toString(8)); // "12"
alert(num.toString(10)); // "10"
alert(num.toString(16)); // "A"
常用方法<br />4.5 number数字
0 12-22 12.5 js中多增加了一个number类型的数据NaN typeof NaN -> Number
var intNum = 55; // 十进制整数 var octalNum1 = 070; // 八进制的56
var octalNum1 = 079; // 无效的八进制数值——解析为79
var octalNum1 = 08; // 无效的八进制数值——解析为8
var hexNum1 = 0xA; // 十六进制的10
var hexNum2 = 0x1F; // 十六进制的31
注意,八进制字面量在严格模式下是无效的,会导致抛出错误。
4.5.1 数值范围
ECMAScript 能够表示的最小数值保存在 Number.MIN_VALUE 中——在多数浏览器中,这个值是 5e-324;能够 Number.MAX_VALUE 中——在大多数浏览器中,这个值是1.7976931348623157e+308。如果某次计算的结果得到了一个超过JavaScript 数值范围的值,那么这个数值将会自动转换为 Infinity 值,如果这个数值是负数,则会转换成 -Infinity(负无穷),如果这个数值是正数,则会转换成Infinity(正无穷)。要确定一个数值是不是有穷的,可以使用 isFinite() 函数。
4.5.2 NaN
- not a numer : 不是一个数,但是属于number类型
- NaN == NaN : false , NaN 和任何其他值都不相等
4.5.3 isNaN()
- 用来检测当前这个值是否是非有效数字,如果不是有效数字,检测的结果是true , 反之是有效数字则为false
isNaN(0) // ->false
isNaN(NaN) // ->true
- 当我们使用isNaN检测值的时候,检测的值不是number类型的,浏览器默认的吧值先转换为number类型,任何再去检测
isNaN('12') //->false
4.5.4 Number()
- 把其他数据类型值转化成number类型的值
Number('12') // -> 12
Number('12px') // ->NaN
// 在使用Number转换的时候只要字符串中出现任何一个非有效数字字符,最后的结果都是NaN
Number(true) //-> 1
Number(false) //-> 0
Number(null) // -> 0
Number(undefined) //->NaN
- 把引用数据类型转换成number,首先需要吧引用数据类型转为字符串(toString),在把字符串转换为number即可
Number([]) // -> ""
Number([12]) // -> 12
Number([12,13]) // -> 12,13 (,是非有效字符) -> NaN
Number({age:12}) // ->NaN
Number({}) // -> NaN
4.5.5 pareInt
- 也是吧其他数据类型值转换为number,和Number方法在处理字符串的时候有所区别
Number('12px') // -> NaN
parseInt('12px') // -> 12
- 提取规则:从左到右依次查找有效数字字符,知道遇到非有效数字字符为止(不管后端是否还有,都不找了)
parseInt('12px13') // -> 12
4.5.6 数值转换
处理整数最常用的还是 parseInt() ,它会忽略字符前面的空格,直到找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt() 就会返回 NaN;也就是说,用 parseInt() 转换空字符串会返回 NaN 。如果第一个字符是数字字符, parseInt() 会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。如果字符以“0x”开头且后面跟数字字符,会被解析为 16 进制整数;<br />以“0”开头且后面跟数字字符,会被解析为 8 进制整数。下面给出一些例子:
var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt(""); // NaN
var num3 = parseInt("0xA"); // 10(十六进制)
var num4 = parseInt(22.5); // 22
var num5 = parseInt("70"); // 70
var num6 = parseInt("0xf"); // 15(十六进制)
4.5.7 pareFloat
- 在pareInt的基础上可以识别小数点
pareInt('12.5px') -> 12
pareFloat('12.5px') -> 12.5
4.6 null 和undefined
- null : 空,没有
- undefined :未定义,没有
- "" : 空字符串,没有
- 0: 也可以理解为没有
4.6.1 空字符串和null的区别
- 都是去去种树
- 空字符串属于去挖了个坑,但是没有种任何东西
- null属于连坑都没挖
- 空字符串相对于null来说开辟了内存地址,消耗了那么一丢丢的性能
4.6.2 null和undefined的区别
- null一般都是暂时没有,预期中以后会有的(可能以后也没有达到预期),在js中null都是手动先赋值为null,后期我们在给其赋具体值
- undefined:完全没有预料之内的
4.7 Object 对象
ECMAScript 中的对象其实就是一组数据和功能的集合。对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定义对象,如下所示:
var o = new Object();
每一个对象都是由零到多组 属性名(key键):属性值(value值) 组成的,或者说有多组键值对组成的,每一组键值对中间用逗号分隔
4.7.1 属性
描述这个对象特点特征的
var obj ={name:'wjw',age:8};
4.7.2 获取
某个属性名对应的属性值或者数字格式的
obj.name
obj['name']
4.7.3 存储
属性值可以是任何数据类型
- 对象名.属性名:忽略属性名的单双引号
- 对象名[属性名]:不能忽略单双引号
// 如果属性名是数字如何操作
obj.0 语法不支持
obj[0] / obj['0'] 两种都支持
如果操作的属性名在对象中不存在,获取的结果是undefined
obj.sex // ->undefined
4.7.4 设置/修改
一个对象的属性名是不能重复的(唯一性),如果之前存在就是修改属性值的操作,反之不存在就是新的设置属性的操作
obj.sex = '男';
obj['age'] = 9;
4.7.5 删除
假删除:让其属性赋值为null,但是属性还是对象
obj.sex = null;
真删除:把整个属性都在对象中暴力移出
delete obj.sex
4.8 基本数据类型 和 引用数据类型的区别
JS是运行在浏览器中的(内核引擎),浏览器会为JS提供赖以生存的环境(提供给js代码执行的环境)=> 全局作用域window(global)
var a = 12;
var b = a; // 把A变量存储的值赋值给B
b = 13;
console.log(a);
var n ={name:'wjh'};
var m = n;
m.name = 'wjw'
console.log(n.name)
- 基本数据类型是按值操作的:基本数据类型的赋值的时候,是直接把值赋值给变量即可
- 引用数据类型是按照空间地址(引用地址)来操作的: var n = {name:'wjw'}
- 先创建一个变量n
- 浏览器首先会吧开辟一个新的存储控件(内存控件),目的是吧对象中需要存储的内容(键值对)分别的存储在这个空间中,为了方便后期找到这个空间,浏览器给空间设定一个地址(16进制)
- 把空间的地址赋值给了变量
4.9 function数据类型
函数数据类型也要按照引用地址来操作的
函数:具备一定功能的方法
// => 创建函数:
function 函数名(){
//=> 函数体:实现某一个功能的具体JS代码
}
// => 执行函数:相当于使用洗衣机洗衣服(如果函数只创建了,但是没有去执行,函数没有任何的意义)
// 函数名()
function fn(){
console.log(1+1);
}
fn; // => 输出函数本身
fn(); // => 把函数执行(吧函数体重实现功能的代码执行)
形参:形式参数(变量),函数的入口<br />当我们创建一个函数想要实现个功能的时候,发现有一些材料不清楚,只有当函数运行的时候,别人传递给我,我才知道,此时我们就需要设定入口,让用户执行的时候通过入口把值把我们
function fn(num1,num2){
console.log(num1+num2)
}
// 实参:函数执行传递给函数的具体值就是实参
fn(1,2);
fn(10,20);
4.10 数据类型转换
把其他数据类型转换为number类型<br />-> isNaN、Number、pareInt、parseFloat<br />-> 在进行数据加减乘除数学运算的时候
// true -> 1 false->0
// ''->0 '12'->12 '12px'->NaN/12
// '小伙子'->NaN
// null -> 0
// undefined-> NaN
{} /^$/ function() ->NaN
[]
[12]->'12'->12
['12,13']->'12,23'->NaN
// => 引用数据类型转换为数字
// 通过toString方法把数组转换为字符串,然后在调用Number转换为数字
4.10.1 JS中的数据运算
- +、-、*、/加减乘除
- 除了加法有特殊性,其余的运算符都是数学运算,也是遇到非数字类型,需要把其转换为number再运算
1-'1' -> 0
10*null -> 0
10/undefined -> NaN
10*[10]->100
4.10.2 加法的特殊性:
- 在遇到字符串的时候,+不是数学运算,而是字符串拼接,只要不遇到字符串就是数学运算
1+'1' -> '11'
null+'1' -> ‘null1’
- 字符串拼接:是把其它的值转换为字符串然后再拼接(toString)
- 其它数据类型的toString是直接的把值用单(双)引号包起来极客,只有对象的特殊性,对象的有特殊性,对象.toStirng==='[Object Object]
4.10.3 将其它数据类型转换为布尔类型
- Boolean、!、!!
- 在条件判断的时候、也是转换为布尔类型,然后验证条件的真假
- 只有0、NaN、空字符串、null、undefined五个转换为false、其余的都是转换为true
[] -> true
-1 -> true
if(box){
// => 首先把box变量存储的值获取到,转化为布尔类型,如果为true条件成立,反之不成立
}
if(3+'3px'){
// 条件成立: 3 + '3px' = '33px'
}
if(3-'3px'){
// 条件不成立: 3-'3px' = NaN
}
4.10.4 在使用==进行比较的时候
在使用==进行比较的时候,如果左右两边的数据不相同,浏览器默认转换为相同的类型,然后在比较('==='不会这样操作)
// 对象和对象: 应用数据类型比较的空间地址不一样,不是一个空间
[] == [] -> false
var a ={}
var b = a;
a==b -> true
4.10.5 对象和数字:吧对象转换成数字
[]== 0 -> true
({})=== NaN -> false
NaN和自己不相等和其它任何值都不相等
4.10.6 对象和字符串:把两边都转换为数字比较的
[]==='' -> true
4.10.7 对象和布尔值:把两边都转换成数字
[]==true -> 0==1 ->false
[]==false -> 0==0 ->true
![]==false -> ![] ->把数组变成为布尔在取反=false
false=false -> true
字符串和数字:字符串转换为数字<br />字符串和布尔:都转为数字<br />布尔和数字:布尔转换为数字
规律:两个等于号比较,左右两边数字值的类型不一样,浏览器会吧两边的类型都转换为数字然后再比较,但是null和undefined除外<br />null==undefined -> true<br />null===undefined -> false<br />null 和 undefined 和其它任何都不相等<br />null==0 -> false null以及undefined和其它任何值都不相等
5 JS中常见的操作的语句
5.1 if、else if 、else
判断操作语句
if(条件1){
//=>条件1成立执行的操作
}else if(条件2){
//=>上面条件不成立,条件2成立,执行的操作
}
...
else{
// => 以上条件都不成立执行的操作
}
如果好几个条件都成立了,只吧第一个成立的条件执行,后面成立的条件忽略不管<br />条件:
A==B、A!=B、A>B、A<B
if(A){} // 先把A转换为布尔类型,判断真假以此来决定是否成立
//否成立
if(A>B&&A<10){} //只有两个小条件都是真,整体条件为真
if(A>B||A<10){} // 只要其中一个小条件成立,整体条件是真
BAT 面试题
var num = parseFloat('width:12.5px');
if(num==12.5){ // =>NaN
alert(12.5);
}else if(num==NaN){ // NaN!=NaN
alert(NaN);
}else if(typeof num=='number'){ //
alert(0)
}else{
alert("啥也不是")
}
5.2 三元运算符
条件?条件成立执行:条件不成立执行
if(条件){}else : 三元运算符就是这种简单if..else..的另一种写法
var num = 10;
if(num>5&&num<10){
num++;//累加1
}else{
num--;
}
// 修改成为三元运算符,如果条件成立或者不成立的某一种情况并不需要什么处理
// 我们空着语法不符合,我们使用null、undefined、void 0(就是undefined)占位就可以
num>5&&num<10?num++:num--;
var num = 10;
if(num>5 && num<10){
num++;
break;/continue;/return;
}
// => 修改成为三元运算符
// 在三元运算符的操作中不能出现break、continue、return这样的关键词,所以我们无法用三目代替if、else
num>5 && num<10?
(num++,return):null;
5.3 swith case
swith case应用于if、else中一个变量在不同值情况下的不同操作
var num =10;
switch(num){
//switch后面小括号中存放的是一个值(一般我们都写变量;把变量存储的值拿来用,有时候也可能是一个计算)
case 1:
// case后面放的都是值,目的验证switch后面的值和哪一种case后面的值相等,相等的进行对应的处理
...
break;
// 每一种case借宿后都要加break借宿当前的判断
case 10:
...
break;
default:
// switch后面的值和每一种case情况对应的值都不相等,执行最后的default,类似于false
...
}
案例分析
var num = 5;
switch(num%2){//=>先把取余操作进行运算,拿运算结果case比较
case 0:
num++;
break; //不加break,不管后面的条件是够成立,都会继续向下执行,知道遇到break为止
// 不加break,就可以实现||这样的操作
case: 2-1: //case后面也应该是值,此处先把2-1计算,把计算的结果和switch值比较
num--;
// 最后一项可以不加break,不加也能跳出判断
break;
}
num%2:让num存储的值除以2去余数(0或者1)
swich case 中的比较实用的"==="
- =:赋值,等号左边是变量,右边是值
- ==:比较,如果左边两边的类型不一样,浏览器默认转换为一样的然后再进行比较 '6'==6 => 6==6 =>true
- ===:绝对相等,不仅要求值一样,并且类型也要完全一样
5.4 循环操作语句
循环,重复做一件事情
for(设置循环起始值;设置循环执行的条件;步长累加){
// 循环体:重复做的事情都是在循环体重
}
- 设置初始值
- 验证条件
- 条件成立,执行循环体:不成立,循环借宿
- 步长累加
for(;i<5;;){
consloe.log(i);
//没有步长累加,我们的i永远是0,循环条件永远成立“死循环”;
//项目中不能出现死循环,一旦出现,循环下面的事情都做不了
}
5.4.1 continue
结束本轮循环,继续执行下一轮:循环体重continue后面的代码都不会在执行,它会直接的去执行步长,然后进行下一轮
for(var i=0;i<5;i+=2){
console.log(i)
continue;
}
5.4.2 break
结束整个循环:循环体重一旦遇到break首先后面代码不执行了,而且步长累加也不执行了,循环都结束了
for(var i=0;i<5;i+=2){
console.log(i)
break;
}
BAT面试题
for(var i=1;i<10;i+=2){
if(i<5){
i++;
continue;
}else{
i+=3;
break;
}
console.log(i)
}
console.log(i) // =>10
5.4.3 for in
用来遍历(循环)对象键值对的
- var key;var attr(attribute);
- 对象中有多少键值对,我们的for in 循环遍历多少次(多少次)
- 第一次循环key这个遍历存储的都是当前循环这个组键值队的属性名
- key存储的值都是字符串格式的(不管属性名是否为数字)
- 在for in 循环的遍历时候,大部分浏览器都是先把对象中的键值对进行排序(把数字属性的排在前面,并且排列的时候安卓数字由小达大排列),其次在把非数字的属性名按照之前编写的顺序,循环的时候按照重新排列循序一次遍历(小数算作字母不要做数字)
var obj = {name:wjw,age:8,0:'wjh',3:'ylp',1:'cx'}
for(var key in obj){
console.log('ok')
// key 属性名 string
console.log(obj.key)
//获取obj中key这个属性对应的值 ->undefined <=> obj['key']
console.log(obj[key]);
//->每一次循环把key变脸存储的值(当前遍历的属性名)获取到放在中括号中,获取obj对应的属性值
}
for(var key in obj){
if(obj.hasOwnProperty(key)){
}
}
6 JS的DOM获取节点
DOM:document object model 文档对象模型,提供一些属性和方法可以让我们去操作DOM元素
6.1 节点介绍
node 节点,浏览器默认在一个html页面中的所有内容都是节点(包括标签、注解、文字文本等)
- 元素节点:HTML标签
- 文本节点:文字内容(大部分浏览器会把空格和换行也当做文本节点)
- 注解节点
- document文档节点
元素节点
- nodeType:1
- 属性含有某个节点的名称
- nodeName: 大写标签名(在部分浏览器的怪异模式下,我们写的标签名是小写,它获取的就是小写...)
- 对于元素节点,因为本身不直接包含文本,所以nodeValue是不可用的。当然你也可以在示例中自己写试试看有什么结果出现。
- 对于文本节点,nodeValue=文本值
- 对于属性节点,nodeValue=属性值
- nodeValue:null
- 对于元素节点,nodeType=1
- 对于文本节点,nodeType=3
- 对于属性节点,nodeType=2
- 对于注释元素,nodeType=8
- 对于文档元素,nodeType=9
[curEle].tagName:获取当前元素的标签名(获取的标签名一般都是大写)
文本节点
nodeType:3<br />nodeName:#text<br />nodeValue:文本内容
注释节点
nodeType:8<br />nodeName:#comment<br />nodeValue:注释内容
文档节点
nodeType:9<br />nodeName:#document<br />nodeValue:null
<-- div#box>(ul>li{0$}*3)+div{内容$}*3-->
<div id="box">
<ul>
<li>01</li>
<li>02</li>
<li>03</li>
</ul>
<div>内容1</div>
<div>内容2</div>
<div>内容3</div>
</div>
6.2 获取dom元素
6.2.1 document.getElementById 一个元素
- 此方法的上下文只能document
- 一个html页面中元素的id理论上是不能重复的,如果页面中ID重复了,我们获得结果第一个id对应的元素对象
- 在ie7更低的版本浏览器中,会把表单元素的name值当做id来识别使用(项目中尽量不要让表单的name和其他元素的id相同)
- 如果我们把js放在结构的下面,我们可以直接使用id值获取这个元素(不需要通过getElementById获取),而且这种方式会把页面中所有id是他的元素都获取到(元素对象,或者集合) => 不推荐
<div id="box1"></div><div id="box2"></div><div id="box1"></div>
<script>
console.log(box1) // -> [div#box1, div#box1, box1: div#box1]
</script>
<input id="myInput" type="text" size="20"/><br />
<script>
var x=document.getElementsByName("myInput");
</script>
6.2.2 document.getElementClassName 元素集合
- 上下文是可以自己来指定
- 获取到的结果是一个元素集合(类数组集合)
- 获取的结果是集合,哪怕集合中只有一项,我们想要操作的是这一项(元素对象),需要先从集合中获取出来,然后再操作
- 但是真实的项目中我们经常会通过样式类名获取元素,getElementClassName这个方法在ie6-8不兼容的
<input name="myInput" type="text" size="20"/><br />
<script>
var x=document.getElementsByName("input");
</script>
var bodyBox = document.getElementsByTagName('body');
bodyBox[0].getElementsByTagName('div');
6.2.3 document.getElementsTagName 元素集合
<input name="myInput" type="text" size="20"/><br />
<script>
var x=document.getElementsByName("input");
</script>
6.2.4 document.getElementsByName 节点集合
通过元素的NAME属性值获取一组元素(类数组:节点集合NodeList)
他的上下文只能是document
- IE浏览器只能识别表单元素的name属性值,所以我们这个方法一般都用来操作表单元素的
- 获取html获得body的元素对象
<input name="myInput" type="text" size="20"/><br />
<script>
var x=document.getElementsByName("myInput");
</script>
6.2.5 domcument.domcumentElement 获取整个html的对象
document.documentElement.clientWidth||document.body.clientWidth
// 获取当前浏览器可视区域的宽度(当前页面一个屏幕的宽度)
// =>clientHieght 获取高度
6.2.6 domcument.body 获取body对象
6.2.7 domcument.head 获取整个head对象
6.2.8 [context]querySelector 一个元素对象 / [context]querySelectorAll 获取元素集合
- ie6-8不兼容,而且没有特别好的办法处理他的兼容,所以这两个方法一般多用于移动端开发使用
querySelector 获取一个元素对象<br />querySelectorAll 获取的一个元素集合<br />只要css支持的选择器,这里大部分都支持
document.querySelector('#box1');
document.querySelectorAll('.box1');
document.querySelectorAll('div');
document.querySelectorAll('body>div');
document.querySelectorAll('#box1 li');
6.3 节点关系属性
节点是用来描述页面中每一部门之间关系的,只要我可以获取页面中的一个页面,那么我就可以通过相关的属性和方法获取页面中所有的节点
6.3.1 childNodes
获取当前元素所有的子节点(节点集合:类数组)
注:不仅仅是元素子节点,文本、注释等都会包含在内:子节点说明只是在儿子辈分中查找
6.3.2 children
获取所有的元素子节点(元素集合)
在IE6-8下获取的结果和标准浏览器中有区别(IE6-8中会把注释点当做元素节点获取到)
6.3.3 pareNode
获取当前元素的父节点(元素对象)
6.3.4 previousibing
获取当前节点的上一个各个节点上一个哥哥节点(不一定是元素节点也可能是文本或者注释)
6.3.5 nextibling
获取当前节点的下一个弟弟节点
6.3.6 previousElementbling
获取当前节点的上一个哥哥元素节点
6.3.7 nextElementsIbling
获取当前节点下一个弟弟元素节点
IE6-8不兼容
6.3.8 firstChild
当前元素所有子节点中的第一个(也不一定是元素节点,可能是文本和注释)
6.3.9 lastChild
当前元素多有子节点中的最后一个
fistElementChild lastElementChild(IE6-8兼容)
6.4 创建和增加DOM元素
真实项目中,我们偶尔会在js中动态创建html标签,然后把其增加到页面中
6.4.1 document.createElement
在js中动态创建一个html标签
6.4.2 appendChild
容器.appendChild(新元素)
把当前创建的新元素添加到容器的末尾位置
6.4.3 inserBefore
容器.inserBefore(新元素、老元素)
在当前容器中,把新创建的元素增加到老元素之前
// 创建
var oDiv = document.createElement('div');
oDiv.id='div1';
oDiv.className = 'box';
// 添加到页面中
document.body.appendChild(oDiv);
document.body.inserBefore(oDiv,box2);
var link = document.createElement('a');
link.href = 'http://www.baidu.com?name=1&age=2#haha'
consloe.dir(link);
// hash:存储饿哈希值 '#haha'
// hostname:域名 'www.baidu.com'
// pathname:路径 '/stu/'
// protocol:协议 'http:'
// search:问号传递参数值 '?nname=1&age=2'
真实项目中很多需要通过动态创建元素来完成的,其中有一个需求:解析一个url地址每一部分的信息(包括问号传值的参数值)
- 纯字符串拆分截取
- 编写强大的正则,捕获到需要的结果
- 通过动态创建一个a标签,利用a标签的一些内置属性来分别获取每一部分的内容
function queryURLParameter(url){
var link = document.createElement('a');
link.href=url;
var search = link.search,
obj = {}'
if(search.length===0) return;
search = search.substr(1).split(/&|=/g);
for(var i=0;i<search.length;i+=2){
var key = search[i],
value = search[i+1];
obj[key]=value;
}
link = null;
return obj;
}
6.5 修改删除克隆DOM元素
6.5.1 removeChild
- 容器.removeChild(元素)
- 在当前容器中把每一个元素移出掉
6.5.2 replaceChild
- 容器.removeChild(新元素,老元素)
- 把原有的元素克隆一份一模一样的,false:只克隆当前元素本身,true:深度克隆,吧当前元素本身以及元素的所有后代都进行克隆
6.5.3 [set/get/remove]Attribute
给当前元素设置/获取/移出属性的(一般操作的都是它的自定义属性)
box.setAttribute('myIndex',0)
box.getAttribute('myIndex')
box.removeAttribute('myIndex')
使用xxx.index=0 和xxx.setAttribute('index',0)这两种设置自定义属性的区别
xxx.index : 是吧当前操作的元素当做一个普通对象,为其设置一个属性名<br />xxx.setAttribute:把元素当做特殊的元素对象来处理,设置的自定义属性是和页面结构中的DOM元素映射在一起的
JS中获取的元素对象,我们可以把他理解为两种角色:
- 与页面HTML结构无关的普通对象
- 与页面HTML结构存在映射关系的元素对象
元素对象中的内置属性,大部分都和页面的标签存在映射关系:<br />xxx.style.backgroundColor = 'xxx' 此时不仅把js中对象对应的属性值改变了,而且也会映射到页面的html标签上(标签中有一个style行内样式,元素的样式改变了)<br />xxx.className = 'xxx'此时不仅是吧js对象中的属性值改变了,而且页面中的标签增加了class样式类(可以看见的)
元素对象中的自定义属性: xxx.index=0<br />仅仅是吧js对象中增加了一个属性名(自定义的),和页面中的html没啥关系(在结构上看不见)
xxx.setAttribute:通过这种方式设置的自定义属性和之前提到的内置属性差不多,都是和html结构存在映射关系的(设置的自定属性可以呈现在结构上)
6.6 面试题
把当前页面中所有id叫做box1的都获取到
var allList = document.getElementsByTagName(*);
var result = []
for(var i=0;i<allList.length;i++){
var item = allList[i];
item.id === 'box1'?result.push(item)
}
console.log(result)
获取当前元素的上一个哥哥元素节点(兼容所有的浏览器)
curEle:current element
// 首先获取当前元素的上一个哥哥节点,判断当前获取的节点是否为元素节点(nodeType===1)
// 如果不是基于当前获取的节点,找他的上一个哥哥节点..(找几次不知道)一直到找到的节点是元素节点为止
// 如果在查找过程中,发现没有上一个哥哥节点,找到头了,则不再继续查找
function prev(curEle){
var p = curEle.previousSibling; // 属性返回同一树层级中指定节点的前一个节点。
while(p&&p.nodeType!==1){ //p:p!=null
p = p.previousSibling;
}
return p;
}
// 扩展
// next: 获取下一个弟弟元素节点
// prevAll:获取所有的哥哥元素节点
// nextAll:获取所有的弟弟元素节点
// siblings:获取所有的兄弟元素节点
// index:获取当前元素的兄弟中排名索引
7 Math的常用方法
7.1 数学函数
但是他是对象数据类型的
- 'object'
- Math对象中给我们提供了很多常用操作数字的方法
- console.dir(Math) // 查看所有方法
7.1.1 abs
Math.abs 取绝对值
7.1.2 cell / floor
cell: 向上取整
floor: 向下取整
7.1.3 round
round: 四舍五入
7.1.4 random
random: 获取一个[0,1]之间的一个随机小数
7.1.5 max/minx
max 获取一组值中的最大值
minx 获取一组值中的最小值
7.1.6 PI
Math.PI 获取圆周率
7.1.7 pow / sqrt
pow 获取一个值的多少幂
sqrt 获取一个值的开平方
8 字符串及常用的方法
在js中用单(双)引号包裹起来的都是字符串
var str = 'welcome to credan!'
字符串就是由零到多个字符串组成的
第一个字符索引0<br />第二个字符索引1<br />...<br />有length的属性,存储的是当前字符串中字符的个数(字符串的长度)
以数字作为索引,从零开始的
str[0] -> 'w' 第一个字符<br />strlength-> 46<br />str[str.length-1] -> '!' 最后一个字符<br />str[100] -> undefined 如果指定的索引不存在获取的结果是undefined
真实项目中,我们经常操作字符串,此时我们需要掌握常用的一些字符床操作方法
console.dir(String.prototype)
charAt && charCodeAt
8.1 str.charCodeAt(索引):
在charAt 基础上,把获取的字符变为Unicode编码值(对应ASCll码表)
- 48-57 0-9
- 65-90 A-Z
- 97-122 a-z
- ...
String.fromCharCode(十进制的Unicode值),把值按照ascll码表中的信息,转为原有字符,charCodeAt正好对应<br />
8.2 substr && substring && slice
- 实现字符串截取的三个办法
- str.substr(n.m) : 从索引n开始,截取m个字符
- str.substring(n,m):从索引n开始,截取到索引为m处(包含m),把找到的部分截取
- str.slice(n,m):和substring语法意义,区别在于slice支持一负数做索引
当索引是负数的时候,浏览器在处理的时候,是用字符串的总长度加上负数索引,然后按照正数处理操作
细节:
- 如果只传递了n(str.substr(n)/str.substring(n)),相当于索引n开始的一直截取到字符串的末尾
- 如果传递的索引超出最大限制,也是吧能截取的部分截取掉即可
- 如果一个参数都不传递:相当于吧证书字符串都截取(字符串的克隆)
indexOf && lastIndexOf
- str.indexOf 获取当前字符在字符串中第一次出现位置的索引
- str.lastIndexOf 获取的是最后一次出现位置的索引
如果当前字符在字符串中没有出现过,结果是-1:我们根据这个规律可言验证一下当前字符串中是否包含某个字符
if(str.indexOf('?')===-1){
// => 没有出现过
}
if(str.indexOf('?')>=-1){
// => 出现过
}
8.3 split
str.split 按照某个字符串分成数组中的某一项,和数组中的join方法是对应
8.4 replace
str.replace 实现字符的替换
执行一次replace 只能替换一次,如果有好几个都需要替换,在不适用正则的情况下,我们需要执行很多次replace
有些需求及时执行很多次repalce也实现不了,此时需要使用正则处理,真实项目中replace一般都是和正则搭配使用的
8.5 trim && trimLeft && trimRight
- str.trimLeft : 去除字符串开始的口空格
- str.trimRight : 去除字符串结尾的口空格
- str.trim 去除字符串首位的空格
获取地址栏的值
function queryURLPrameter(url){
// => url 传递的参数
var quesIndex = url.indexOf('?'),
obj = {}
if(quesIndex === -1){ // url中没有问号传参 直接返回空
retrun obj;
}
url = url.substr(quesIndex + 1);
var ary = url.split('&');
for(var i =0;i<ary.length;i++){
var curAry = ary[i].split('=');
obj[curAry[0]] = curAry[i];
}
return obj
}
String.prototype.myQueryURLParameter = function
myQueryURLParamter(){
var obj = /([^=?&]+)=([^=?&]+)/g;
this.replace(reg,function(){
var arg = argments;
obj[arg[1]] = arg[2]
})
return obj;
}
var str = 'https://www/baidu.com/s?wd=1233213&issp=1';
console.log(str.myQueryURLParameter());
9 Date的基础知识
9.1 Data 是日期类
通过它可以对时间进行处理
var time = new Date();
// 获取当前客户端本机时间(当前获取的时间不能作为重要的参考依据)
// 获取结果是一个日期格式的对象
// Wed Mar 20 2019 17:37:16 GMT+0800 (中国标准时间)
typeof new Date() -> object
time.getFullYear() 获取四位数整年
time.getMonth() 获取月份
time.getDate() 获取日
time.getDay() 获取星期(0-6代表周日-周六)
time.getHours() 获取小时
time.getMinutes() 获取分钟
time.getSeconds() 获取秒
time.getMilliseconds() 获取毫秒
time.getTime() 获取当前日期距离'1970-01-01 00:00:00'的毫秒差
var time = new Date('2017-10-22');
// 当new Date 中传递一个时间格式的字符串,相当于把这个字符串换位标准时间对象
// (转换完成后,就可以调取上面我们讲的那些方法)
// 时间格式的字符串<br />'2017-10-22' (IE下识别不了)<br />'2017/10/22 16:15:34'<br />'1508659621314'(如果传递的是距离1970年那个毫秒查,也可以识别转换的,但是只能是数字,不能是字符串)<br />
10 数组的函数
10.1 数组的基础结构
- 数组也是对象数据类型 typeof [] -> 'object'
- 数组也是属性名,只不过属性名是数字,我们把数字属性名称之为它的索引:数组是以数字为索引,索引从零开始,有一个length属性代表数组的长度
<br />
类数组:类似于数组,但是不是数组
- 通过getElementsByTageName 获取的元素集合是类数组
- 函数中的实参集合argument也是类数组
<br />
// for 循环操作
for(var i =0;i<ary.length;i++){
console.log(ary[i])
}
// for in 循环操作
for(var key in ary){
// key:属性名(数组中的属性名是索引)
console.log(ary[key]);
}
// for 循环只能遍历到数组私有的一些属性,而for in 循环可以吧一些自定义的公共属性也能遍历到
10.2 数组中的常用方法
数组中有很多方法
console.dir(Array.prototype)
- 方法的意义和作用
- 方法的形参
- 方法的返回值
- 通过此方法,原来的数组是否发生了改变
实现数组的增加、修改、删除
10.2.1 增加
push : 向数组的末尾追加新内容
参数:一到多个,任何数据类型都可以,想要给数组末尾追加什么,直接传递到push方法中极客,传递多个逗号隔开<br />返回值:新增后数组的长度<br />原有数组改变了
unshift:向数组开头追加新内容
参数:需要追加的内容(可以是多个任何数据类型的值)<br />返回值:新增后 数组的长度<br />原来数组改变了
把数组当做一个普通的对象,使用对象键值对的操作,给其设置新的属性(索引)<br />ary[ary.length]=xxx 向数组的末尾追加了新的内容
10.2.2 删除
pop 删除数组最后一项
参数:无<br />返回值:被删除的那一项内容<br />原有数组改变了
shift:删除数组第一项
参数:无<br />返回值:被删除那一项的内容<br />原有数组改变了<br />使用shift删除第一项之后,后面每一项的索引都要向前进一位(导致后面项的索引发生了改变)
把数组当做一个普通对象操作<br />delete删除:delete ary[索引]删除指定索引这一项(当前项被删除后),原有数组其它项的索引不会改变:当前数组的length也不会改变
ary.length--:删除数组最后一项
splice:数组中内置的方法,可以实现数组的增加、修改、删除
splice实现删除<br />splice(n,m):从索引n开始删除m个(m不写是个删除列数组的末尾,n也不写)<br />返回值:被删除的内容(以一个新数组保存)<br />原有数组改变了<br />splice(0) 清空数组<br />splice() 一项都不删除,返回一个新的空数组<br />splice(0,1)删除第一项
splice实现修改
splice(n,m,x):在原有删除的基础上,用x代替删除的内容
splice实现增加<br />splice(n,0,x):在修改的基础上,我们一项都不删除,把x插入到索引n的前面<br />ary.splice(0,0,x) 向数组末尾追加新元素
数组的查询
slice 数组的查询
- 参数:slice(n,m) 从索引n开始找到索引m处(不包含m)
- 返回值:吧找到的部分已一个新数组返回
- 原来的数组不变
slice(n) 从索引n开始找到末尾<br />slice(0) /slice() 数组克隆,克隆一份和原来数组一模一样的的新数组<br />slice支持负载索引,如果传递的索引为负数,浏览器解析的时候是按照总长度+负数索引 来处理的
将两个数组进行拼接
concat:将多个数组拼接在一起
- 参数:要拼接的内容(把内容放在原数组的后面),可以是一个数组
- 返回:拼接后的新数组
10.3 数组的查询
slice: 数组的查询<br />参数: slice(n,m) 从索引n开始找到索引为m处(不包含m)<br />返回值:把找到的部分已一个新数组返回<br />原来的数组不变
slice(n) 从索引n开始找到末尾
slice(0) / slice() 数组克隆,克隆一份和原来数组一模一样的新数组
slice 支持负数索引,如果传递的索引为负数,浏览器解析的时候是按照,总长度+负数索引 来处理的
10.4 数组的拼接
concat:将多个数组拼接在一起<br />参数:要拼接的内容(把内容放在原数组的后面),可以是一个数组,也可以是一些数据值<br />返回:拼接后的新数组<br />原有的数组不变
let arr = [0,100]
arr.concat([100,200],[200,300],12)
concat() 什么都没有拼接,相当于吧原有数组克隆一份一模一样的出来<br />10.5 数组转字符串
10.5.1 toString
实现吧数组转化为字符串(转换后的字符串逗号分隔每一项)
参数:无<br />返回值:转换的字符串<br />原有数组不变
10.5.2 join
把数组按照指定的分隔符转换为字符串,和字符串中的split相对应
- 参数:指定的链接符号
- 返回值:转换后的字符串
- 原有数组不变
已知数组的每一项都是数字,想事先数组求和,我们如何实现?
循环实现
var total = null;
for(var i=0;i<ary.length;i++){
total+=ary[i];
}
利用join
var total = eval(ary.join('+')) // evel:把字符串变为js表达式执行
10.6 实现数组中每一项的排序和排列
10.6.1 reverse
把数组中每一项倒过来排序
参数:无<br />返回值:排序后的数组<br />原有数组改变
10.6.2 sort
实现数组的排序
参数:无或者回调函数<br />返回值:排序后的数组<br />原有数组改变<br /> 不传递参数的情况下:可以给10以内的数字进行升序排列,但是超过10的就无法处理(多位数值识别第一位)
ary.sort(function(a,b){
return a-b; //升序
return b-a; //降序
})
10.7 验证数组中是否包含某一项
10.7.1 indexOf / lastindexOf
获取当前现在数组中第一次或者最后一次出现的位置索引
- 数组的这两个方法在IE6-IE8下不兼容
- 字符串的这两个方法兼容所有的浏览器
if(ary.indexOf(12)>-1){
// 数组中包含12
}
10.7.2 原理
Array.prototype.myIndexOf = function myIndexOf(value){
var result = -1;
for(var i =0;i<this.length;i++){
if(value===this[i]){
result = i;
break;
}
}
return result;
}
10.8 遍历数组每一项的方法
以下方法ie6-8下都不兼容
10.8.1 forEach
遍历数组中的每一项
ary.forEach(function(value,index){
/* 数组中有多少项,当前回调函数执行多少次,妹妹一次传进来的value就是当前遍历数组这一项的值,index
就是遍历这一项的索引
*/
})
10.8.2 map
遍历数组中的每一项,在forEach的基础上,可以修改每一项的值
ary.map(function(value,index){
/* 数组中有多少项,当前回调函数执行多少次,妹妹一次传进来的value就是当前遍历数组这一项的值,index
就是遍历这一项的索引
*/
return xxx;
// return 后面返回的结果就是当前遍历的这一项修改为xxx
})
filter<br />find<br />reduce<br />every<br />...
10.9 数组去重
var ary = [1,2,3,4,5,6,7,1,3,4,5];
方案一 最简单数组去重法
遍历数组的每一项,拿每一项和它后面的项依次比较,如果相同了,则把相同的这一项在原来数组中删除即可
/*
* 新建一新数组,遍历传入数组,值不在新数组就push进该新数组中
* IE8以下不支持数组的indexOf方法
* */
function uniq(array){
var temp = []; //一个新的临时数组
for(var i = 0; i < array.length; i++){
if(temp.indexOf(array[i]) == -1){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,2,4,9,6,7,5,2,3,5,6,5];
console.log(uniq(aa));
方案二 对象键值法去重
/*
* 速度最快, 占空间最多(空间换时间)
*
* 该方法执行的速度比其他任何方法都快, 就是占用的内存大一些。
* 现思路:新建一js对象以及新数组,遍历传入数组时,判断值是否为js对象的键,
* 不是的话给对象新增该键并放入新数组。
* 注意点:判断是否为js对象键时,会自动对传入的键执行“toString()”,
* 不同的键可能会被误认为一样,例如n[val]-- n[1]、n["1"];
* 解决上述问题还是得调用“indexOf”。*/
function uniq(array){
var temp = {}, r = [], len = array.length, val, type;
for (var i = 0; i < len; i++) {
val = array[i];
type = typeof val;
if (!temp[val]) {
temp[val] = [type];
r.push(val);
} else if (temp[val].indexOf(type) < 0) {
temp[val].push(type);
r.push(val);
}
}
return r;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
方案三 排序后相邻去除法
/*
* 给传入数组排序,排序后相同值相邻,
* 然后遍历时,新数组只加入不与前一值重复的值。
* 会打乱原来数组的顺序
* */
function uniq(array){
array.sort();
var temp=[array[0]];
for(var i = 1; i < array.length; i++){
if( array[i] !== temp[temp.length-1]){
temp.push(array[i]);
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
方案四 数组下标法
/*
*
* 还是得调用“indexOf”性能跟方法1差不多,
* 实现思路:如果当前数组的第i项在当前数组中第一次出现的位置不是i,
* 那么表示第i项是重复的,忽略掉。否则存入结果数组。
* */
function uniq(array){
var temp = [];
for(var i = 0; i < array.length; i++) {
//如果当前数组的第i项在当前数组中第一次出现的位置是i,才存入数组;否则代表是重复的
if(array.indexOf(array[i]) == i){
temp.push(array[i])
}
}
return temp;
}
var aa = [1,2,"2",4,9,"a","a",2,3,5,6,5];
console.log(uniq(aa));
方案五 优化遍历数组法
// 思路:获取没重复的最右一值放入新数组
/*
* 推荐的方法
*
* 方法的实现代码相当酷炫,
* 实现思路:获取没重复的最右一值放入新数组。
* (检测到有重复值时终止当前循环同时进入顶层循环的下一轮判断)*/
function uniq(array){
var temp = [];
var index = [];
var l = array.length;
for(var i = 0; i < l; i++) {
for(var j = i + 1; j < l; j++){
if (array[i] === array[j]){
i++;
j = i;
}
}
temp.push(array[i]);
index.push(i);
}
console.log(index);
return temp;
}
var aa = [1,2,2,3,5,3,6,5];
console.log(uniq(aa));
11 函数基础
- 函数是指一段在一起的、可以做某一件事儿的程序。也叫做子程序、(OOP中)方法
- 函数实现某一个功能的方法
11.1 创建函数
functoin [函数名](){
// => [函数体]
// 实现功能的具体js代码
}
11.2 执行函数
函数名(); // 创建的函数执行,而且这个函数可以执行很多次
函数名();
每一次执行都相当于把函数体重实现功能的js代码重复执行了一遍
在真实的项目中,我们一般都会把实现一个具体功能的代码封装到函数中
- 如果当前这个功能需要在页面中执行多次,不封装成为函数,每一次实现这个功能,都需要重新吧代码写一遍,浪费时间;而封装在一个函数中,以后想实现多次这个功能,我们就没有必要在重新写代码,只需要把函数重新的执行即可,提高了开发效率
- 封装在一个函数,页面中就基本上很难重复一样的代码了,减少了页面中代码的冗余度,提高了代码的重复利用率:
低耦合高内聚
我们把以上的特点成为函数封装 (OOP面向对象编程思想,需要我们掌握的就是类的继承、封装、多态)
11.3 JS中函数的核心原理
函数作为js中引用数据类型中的一种,也是按照引用地址操作的
function sum(){
var total = 1+1;
total *= 20;
console.log(total.toFixed(2));
}
sum();
11.3.1 创建函数
- 首先会在当前作用中声明一个函数名(声明的函数和使用var声明变量是一样的操作:var sum;function cum;这两个名字算重复了)
- 浏览器首先会开辟一个新的内存空间(奉陪一个16进制地址),把函数体重写好的代码当做普通字符串存储在这个内存空间(创建一个函数如果不执行,函数没有意义)
- 把内存空间的地址赋值给之前声明的那个函数名
11.3.2 函数执行
- 目的:把之前存储的实现具体功能的js代码执行
- 函数执行,浏览器首先会为其开辟新的
私有作用域
(只能执行函数中之前编写的js代码) - 形参赋值
- 私有作用中的变量升级
- 把之前穿件时间存储的那些js代码字符串,拿到自由作用域中,然后把题目变成js表达式从上到下执行
- 私有作用域是否销毁的问题
<br />
<br />
11.3.3 闭包
函数执行会形成一个私有的作用域,让里面的私有变量和外界互不影响(相互干扰、外面的无法直接获取里面的变量值),此时我们可以理解为私有作用域把私有变量保护起来,我们把这种保护机制称为为
闭包
11.3.4 栈内存
作用域(全局作用域/私有作用域):提供一个供js代码执行的环境
11.3.5 堆内存
所有的引用数据类型,他们需要存储的内容都是堆内存中(相当于一个仓库,目的是存储信息)
- 对象会吧键值队存储起来
- 函数会把代码当做字符串存储起来
11.4 函数中形参和实参
- 形参:相当于生成洗衣机的时候提供的入口,需要用户执行函数的时候把需要的值传递进来,形参是个变量,用来春初和接口那些值
- 实参:用户执行的时候传递给形参的具体指
// 随便求出两个数的和
function sum(num1,num2){ //num1/num2就是形参变量(类似于var了一下)
var total = num1 + num2;
total*=10;
total=total.toFixed(2);
console.log(total);
}
sum(10,20);//10/20是实参 num1=10 num2=20
sum(10); // num1=10 num2=undefined 定义了形参但是执行的时候,没有传递实参,默认实参就是undefined
11.5 arguments实参集合
当我们不知道用户具体要传递几个值的时候(传递几个值都行),此时我们无法设置形参的个数:遇到此类需要,需要使用函数内置的实参集合:arguments
- argument 只有函数才有
- 不管执行函数的时候是否传递实参,arguments天生就纯在,没有传递实参ARG是个空的集合传递了ARG中包含了所有传递的实参值
- 不管是否设置了形参,ARG中始终存储了所有的实参信息
function sum(){
console.log(arguments)
}
sum(10,20,'wjh',{name:'wjw'});
- arguments 是个类数组集合
- 以数字作为索引(属性名),从0开始
- arguments[0] 第一个实参信息
- arguments[2] 第三个实参信息
- arguments[n] 第n+1个实参信息
- 有一个length的属性,存储的是当前几个的长度(当前传递实参的个数)
- arguments.length
- arguments['length']
- arguments.calle 存储的是当前函数本身
- arguments.calle.caller 存储的是当前函数只在哪执行的(宿主函数),在全局作用域下执行的,结果是null
function sum(){
console.log(arguments.callee.caller);//f
}
function fn(){
sum(10,20,'wjh',{name:'wjw'});
}
fn();
// arguments.call或者arguments.call.caller一般真正项目中很少使用,因为是在严格js模式下不允许我们直接使用这两个属性,然而现有项目大部分都是基于严格模式来的
// 任意数求和
function sum(){
var total = null;
for(var i =0;i<arguments.length;i++){
var cur = Number(arguments[i]);
!isNaN(cur)?total += cur : null
}
consloe.log(total);
return total;
// return 后面跟着的都是值(返回的都是值):此处不少TOTAL变量返回,而是吧total存储到值返回而已
// return 60;
}
sum(10,20,20);
sum();
sum(10,20,30,'wjw')
// console.log(total);
//=>Uncaught ReferenceError: total is not defined 闭包的保护机制导致作用域会保护里面的私有变量
11.6 JS中的返回值return
返回值是函数提供的一个出口:我们如果想在外面使用函数私有的一些信息,那么就需要通过return,把这些信息返回出来供外面使用
sum:代表的是函数本身<br />sum() 让函数先执行,代表的是当前函数返回的结果(return)后面是啥,相当于函数返回的是啥
function sum(){
var total = 0;
renturn
}
console.log(sum());
// 如果函数中没有return或者return后面啥也没有,默认返回的结果是undefined
function sum(){
var total = 0;
renturn;
console.log(sum());
// 函数体重遇到return后,return后面的代码都不在执行了
}
11.7 js中匿名函数
没有名字的函数
- 函数表达式
- 自执行函数
oBox.onclick = function(){
// 把一个码云名字的函数(有名字的也无所谓)作为值赋值给一个变量或者一个元素的某一个事件等,函数表达式
}
(function(n){
// 创建函数和执行函数放在一起,穿件完成立马之执行:自执行函数
// n 形参 n=10
})(10)
// 以下都是自执行函数,符号只有控制语法规范
~function(){}(10)
-function(){}(10)
+function(){}(10)
!function(){}(10)