JavaScript基础入门笔记

0 介绍

0.1 ECMAScript

ECMA-262 的第 5 版是 JS 的第一个稳定版本,得到了各浏览器厂商的支持。

  • 语法
  • 类型
  • 语句
  • 关键词
  • 保留字
  • 操作符
  • 对象

0.2 DOM

文档对象模型是针对 XML 但经过扩展用于 HTML 的 API 。DOM 把整个页面映射为一个多层次节点结构。HTML 或 XML 页面中的每个组成部分都是某种类型的节点,这些节点又包含着不同类型的数据。
document object model 文档对象模型,里面提供了一些属性和方法,可以让我们操作页面的元素

0.3 BOM

原则上讲,BOM只处理浏览器窗口和框架,但下面一些针对浏览器的 JS 扩展也被看做是BOM的一部分。
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 中的一些不确定行为将得到处理,而且队某些不安全的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加如下代码:
这行代码看起来像是字符串,而且也没有赋值给任何变量,但其实它是一个编译指 示(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中很多字都是有特殊含义的,我们这些词,叫做关键词;现在没有特殊含义,以后可能会作为关键的,我们叫做保留字;二关键词和保留字不可以随便用来命名;



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"
复制代码

常用方法

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 进制整数;
以“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(); // => 把函数执行(吧函数体重实现功能的代码执行)
复制代码

形参:形式参数(变量),函数的入口
当我们创建一个函数想要实现个功能的时候,发现有一些材料不清楚,只有当函数运行的时候,别人传递给我,我才知道,此时我们就需要设定入口,让用户执行的时候通过入口把值把我们

function fn(num1,num2){
	console.log(num1+num2)
}
// 实参:函数执行传递给函数的具体值就是实参
fn(1,2);
fn(10,20);
复制代码

4.10 数据类型转换

把其他数据类型转换为number类型
-> isNaN、Number、pareInt、parseFloat
-> 在进行数据加减乘除数学运算的时候

// 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
复制代码

字符串和数字:字符串转换为数字
字符串和布尔:都转为数字
布尔和数字:布尔转换为数字

规律:两个等于号比较,左右两边数字值的类型不一样,浏览器会吧两边的类型都转换为数字然后再比较,但是null和undefined除外
null==undefined -> true
null===undefined -> false
null 和 undefined 和其它任何都不相等
null==0 -> false null以及undefined和其它任何值都不相等

5 JS中常见的操作的语句

5.1 if、else if 、else

判断操作语句

if(条件1){
	//=>条件1成立执行的操作
}else if(条件2){
	//=>上面条件不成立,条件2成立,执行的操作
}
...
else{
	// => 以上条件都不成立执行的操作
}
复制代码

如果好几个条件都成立了,只吧第一个成立的条件执行,后面成立的条件忽略不管
条件:

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
nodeName:#text
nodeValue:文本内容

注释节点

nodeType:8
nodeName:#comment
nodeValue:注释内容

文档节点

nodeType:9
nodeName:#document
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 获取一个元素对象
querySelectorAll 获取的一个元素集合
只要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 : 是吧当前操作的元素当做一个普通对象,为其设置一个属性名
xxx.setAttribute:把元素当做特殊的元素对象来处理,设置的自定义属性是和页面结构中的DOM元素映射在一起的

JS中获取的元素对象,我们可以把他理解为两种角色:

  • 与页面HTML结构无关的普通对象
  • 与页面HTML结构存在映射关系的元素对象

元素对象中的内置属性,大部分都和页面的标签存在映射关系:
xxx.style.backgroundColor = 'xxx' 此时不仅把js中对象对应的属性值改变了,而且也会映射到页面的html标签上(标签中有一个style行内样式,元素的样式改变了)
xxx.className = 'xxx'此时不仅是吧js对象中的属性值改变了,而且页面中的标签增加了class样式类(可以看见的)

元素对象中的自定义属性: xxx.index=0
仅仅是吧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
第二个字符索引1
...
有length的属性,存储的是当前字符串中字符的个数(字符串的长度)

以数字作为索引,从零开始的

str[0] -> 'w' 第一个字符
strlength-> 46
str[str.length-1] -> '!' 最后一个字符
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正好对应

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 中传递一个时间格式的字符串,相当于把这个字符串换位标准时间对象
// (转换完成后,就可以调取上面我们讲的那些方法)
复制代码

// 时间格式的字符串
'2017-10-22' (IE下识别不了)
'2017/10/22 16:15:34'
'1508659621314'(如果传递的是距离1970年那个毫秒查,也可以识别转换的,但是只能是数字,不能是字符串)

10 数组的函数

10.1 数组的基础结构

  • 数组也是对象数据类型 typeof [] -> 'object'
  • 数组也是属性名,只不过属性名是数字,我们把数字属性名称之为它的索引:数组是以数字为索引,索引从零开始,有一个length属性代表数组的长度


类数组:类似于数组,但是不是数组

  • 通过getElementsByTageName 获取的元素集合是类数组
  • 函数中的实参集合argument也是类数组


// 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方法中极客,传递多个逗号隔开
返回值:新增后数组的长度
原有数组改变了

unshift:向数组开头追加新内容

参数:需要追加的内容(可以是多个任何数据类型的值)
返回值:新增后 数组的长度
原来数组改变了

把数组当做一个普通的对象,使用对象键值对的操作,给其设置新的属性(索引)
ary[ary.length]=xxx 向数组的末尾追加了新的内容

10.2.2 删除

pop 删除数组最后一项

参数:无
返回值:被删除的那一项内容
原有数组改变了

shift:删除数组第一项

参数:无
返回值:被删除那一项的内容
原有数组改变了
使用shift删除第一项之后,后面每一项的索引都要向前进一位(导致后面项的索引发生了改变)

把数组当做一个普通对象操作
delete删除:delete ary[索引]删除指定索引这一项(当前项被删除后),原有数组其它项的索引不会改变:当前数组的length也不会改变

ary.length--:删除数组最后一项

splice:数组中内置的方法,可以实现数组的增加、修改、删除

splice实现删除
splice(n,m):从索引n开始删除m个(m不写是个删除列数组的末尾,n也不写)
返回值:被删除的内容(以一个新数组保存)
原有数组改变了
splice(0) 清空数组
splice() 一项都不删除,返回一个新的空数组
splice(0,1)删除第一项

splice实现修改

splice(n,m,x):在原有删除的基础上,用x代替删除的内容

splice实现增加
splice(n,0,x):在修改的基础上,我们一项都不删除,把x插入到索引n的前面
ary.splice(0,0,x)  向数组末尾追加新元素

数组的查询

slice 数组的查询
  • 参数:slice(n,m) 从索引n开始找到索引m处(不包含m)
  • 返回值:吧找到的部分已一个新数组返回
  • 原来的数组不变

slice(n) 从索引n开始找到末尾
slice(0) /slice() 数组克隆,克隆一份和原来数组一模一样的的新数组
slice支持负载索引,如果传递的索引为负数,浏览器解析的时候是按照总长度+负数索引 来处理的

将两个数组进行拼接

concat:将多个数组拼接在一起

  • 参数:要拼接的内容(把内容放在原数组的后面),可以是一个数组
  • 返回:拼接后的新数组

原有数组不变

10.3 数组的查询

slice: 数组的查询
参数: slice(n,m) 从索引n开始找到索引为m处(不包含m)
返回值:把找到的部分已一个新数组返回
原来的数组不变

slice(n) 从索引n开始找到末尾
slice(0) / slice() 数组克隆,克隆一份和原来数组一模一样的新数组
slice 支持负数索引,如果传递的索引为负数,浏览器解析的时候是按照,总长度+负数索引 来处理的
复制代码

10.4 数组的拼接

concat:将多个数组拼接在一起
参数:要拼接的内容(把内容放在原数组的后面),可以是一个数组,也可以是一些数据值
返回:拼接后的新数组
原有的数组不变

let arr = [0,100]
arr.concat([100,200],[200,300],12)
复制代码

concat() 什么都没有拼接,相当于吧原有数组克隆一份一模一样的出来

10.5 数组转字符串

10.5.1 toString

实现吧数组转化为字符串(转换后的字符串逗号分隔每一项)

参数:无
返回值:转换的字符串
原有数组不变

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

把数组中每一项倒过来排序

参数:无
返回值:排序后的数组
原有数组改变

10.6.2 sort

实现数组的排序

参数:无或者回调函数
返回值:排序后的数组
原有数组改变
      不传递参数的情况下:可以给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
find
reduce
every
...

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表达式从上到下执行
  • 私有作用域是否销毁的问题

![image.png](https://cdn.nlark.com/yuque/0/2019/png/271124/1553076800854-5e22d4eb-aa50-40a1-a8de-6abbd5cf8939.png#align=left&display=inline&height=203&name=image.png&originHeight=406&originWidth=986&size=115231&status=done&width=493)

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:代表的是函数本身
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)
复制代码

转载于:https://juejin.im/post/5caaf9516fb9a05e6538be10

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值