JavaScript基础学习笔记

javascript 基础学习笔记

前端开发常用浏览器

  • 谷歌浏览器(Chrome):webkit内核(v8引擎)

    safari、大部分国产浏览器(360、QQ、UC……)、安卓和iOS大部分手机浏览器……

  • 火狐浏览器(Firefox):gecko内核

  • 欧朋浏览器(Opera):presto内核

  • IE浏览器:trident内核

浏览器内核

作为前端开发,我们会使用HTML/CSS/JS编写代码,编写代码的时候要遵循一些规范(W3C) 
浏览器 
浏览器开发商开发的浏览器,目的就是为了按照W3C的规范,识别出开发者编写的代码,并且在页面中绘制出开发者预想的页面和效果(GPU:显卡),我们把浏览中识别代码绘制页面的东西称为‘浏览器的内核或者渲染引擎’

浏览器兼容: 
1、W3C发布的规范都是开发者们不断尝试总结下来的产物,例如:谷歌浏览器开发了一个新的CSS属性(border-radius)可以让开发者快速实现盒子圆角 
-webkit-border-radius 
火狐浏览器发现这个功能很好用,也实现了这个属性 
-moz-border-radius 
…… 
W3C把他融入到规范的时候 
border-radius

2、每个浏览器为了彰显自己的不一样,不按照标准来,但是吧标准中规定的效果用另外一种方式实现了 
window.getComputedStyle 
currentStyle 
这样我们写的时候需要写两套代码

link与@import的区别

1、link是XHTML标签,除了加载css外,还可以定义RSS等其他事物;@import属于CSS范畴,只能加载CSS。 
2、link引用css时,在页面载入是同时加载;@import需要页面完全载入以后加载。 
3、link是XHTML标签,没有兼容问题;@import是在css2.1提出的,低版本的浏览器不支持。 
4、link支持使用JavaScript控制DOM去改变样式;而@import不支持。

JS中提供的浏览器弹框

1、alert:在浏览器中弹出一个提示框(提供确定按钮,点击确定弹框消失)

alert({name:’meng’}); //=>”[object object]” 
alert([12, 13]); //=>”12,23” 
使用alert弹框提示信息,提示的内容最后都会被转换为字符串输出(调用了toString方法) 
alert(1+1);//=>”2”

2、confirm:在alert基础上增加了让用户选择性的操作(提供了两个按钮:确定和取消)

var meng = confirm(‘are you sure delete this!’); 
当用户点击确定的时候,我们接收到的结果是true,点击的是取消按钮我们接收到的结果是false,此后我们可以根据接收的结果做不同的处理即可

3、prompt:在confirm基础上增加让用户输入的效果

var flag = prompt(‘are you sure delete this!’); 
用户点击取消按钮,我们获取到的结果是null,如果用户点击的是确定按钮,我们将会获取到用户输入的内容(如果用户没有输入任何内容,获取的结果是空字符串) 
真实项目中,有确实需要样式精美的网站中,我们的提示框一般都是自己封装插件和组件来实现的,不会用内置的(使用原生js封装模态框组件)

控制台输出

方便开发人员进行调试的 
F12:打开浏览器的控制台 
console.log:在控制台输出,优势不会转换数据类型,输出什么格式的数据都可以 
例: console.log({name:’mengxianjun’}); =>{name:’mengxianjun’} 
console.dir:比log输出的更加详细一些 
console.table:把json数据展示成为一个表格


JavaScript

ECMAScript(ES):规定了JS的一些基础核心的知识(变量、数据类型、语法规范、操作语句等)3/5==6/7 
DOM:document object model文档对象模型,里面提供了一些属性和方法,可以让我们操作页面中的元素 
BOM:browser object model 浏览器对象模型,里面提供了一些属性和方法,可以让我们操作浏览器

变量和常量

变量:值是可以变的 
常量:值是不可变的

 

//=>JS中定义变量的方法

//var 变量名 = 值; (ES6中定义变量使用let)

var num = 12;

var str = "meng";

//=>JAVA等后台语言定义变量比较严谨,js比较松散

int num = 12;

float num = 12.5;

double num = 3.1415926;

console.log(num); //=>12 变量其实只是一个无意义的名字,他所代表的意义都是其存储的那个值

num = 13; //=>让原有的num变量存储的值修改为13(变量只能代表一个值)

 

//=>任何一个具体的数据值都是常量,例如:12就是一个常量

//=>和变量类似,我们设置一个常量(也就是一个名字),给其存储一个值,但是这个存储的值不能修改

const num = 12; //=>定义一个常量num,给他存储了12

num = 13; //=> Uncaught TypeError: Assignment to constant variable. 常量存储的值是不能修改的


JS中的命名规范

1、JS中严格区分大小写

 

var test = 'meng';

var Test = 'xianjun';

console.log(test); //=>'meng' test和Test是两个不同的变量

2、遵循国际命名规则‘驼峰命名法’

第一个单词首字母小写,其余每一个有意义单词首字母大写

 

var studentInfo;

命名使用英文单词,不要使用拼音

不是所有单词都能简写,我们需要保证大家看到名字后知道所代表的意思

add / insert / create : 增加 创建 插入

remove / rm / clear / del / delete : 删除

update : 修改

get / query / select : 查询获取

……

3、命名的时候可以使用$ 、_ 、数字 、字母,但是数字不能作为名字的第一位

 

var student_info;

var $xxx; //=> 一般都是应用JQ获取到的值

var _xxx; //=>一般这样的情况代表变量是一个全局或者通用的变量

4、JS中很多的词都是有特殊含义的,我们这些词叫作“关键字”;现在没有特殊含义,以后可能会作为关键词的,我们叫作“保留字”;而关键字和保留字都不可以随便用来命名;

ECMA-262描述了一组具有特定用途的关键字。这些关键字可用于表示控制语句的开始或结束,或者用于特定操作等。按照规则,关键词也是语言保留的,不能用作标识符。以下就是ECMAScript的全部关键字(带*号上标的是第5版新增的关键字):

breakdoinstanceoftypeof
caseelsenewvar
catchfinallyreturnvoid
continueforswitchwhile
debuger*functionthiswith
defaultifthrowdelete
intry

ECMA-262还描述了另外一组不能用作标识符的保留字。尽管保留字在这门语言中还没有任何特定的用途。但它们有可能在将来被用作关键字。以下是ECMA-262第3版定义的全部保留字:

abstractenumintshort
booleanexportinterfacestatic
byteextendslongsuper
charfinalnativesynchronized
classfloatpackagethrows
constgotoprivatetransient
debuggerimplementsprotectedvolatile
doubleimportpublic

第5版包在非严格模式下运行时的保留字缩减为下列这些:

classenumextendssuper
constexportimport

在严格模式下,第5版还对以下保留字施加了限制:

implementspackagepublicinterface
privatestaticletprotected
yield

JS中的数据类型

  • 基本数据类型(值类型)

    • number:数字 
      +NaN 
      +infinity

    • string:字符串

    • boolean:布尔

    • null:空对象指针

    • undefined:未定义

    • symbol 唯一值 
      +static symbol 
      +symbol.prototype 
      +bigint

  • 引用数据类型

    • object对象数类型

      • {}普通对象

      • []数组

      • /^$/ 正则

      • 日期对象

      • JSON对象

      • Set

      • Map

      • ……

    • function函数数据类型

      • 普通函数

      • 构造函数

      • 箭头函数

      • 生成器函数

      • ……

 

number:数字

12 12.5 -12

string:字符串

'mengxianjun' "孟献军" =>单双引号包裹起来的都是字符串(单双引号没有区别)

Boolean:布尔

true false => 布尔类型:只有两个值

null:空对象指针

undefined:未定义

{name:'meng',age:26}

[12,13,14]

function fn(){}

这么多数据类型JS如何的去检测呢?

  • typeof:检测数据类型的运算符

  • instanceof:检测某个实例是否属于这个类

  • constructor:获取当前实例的构造器

  • object.prototype.toString.call:获取当前实例的所属类信息

typeof

使用typeof检测,返回的结果是一个字符串,字符串中包含的内容证明了值是属于什么类型的 
[局限性] 
1、typeof null 不是“null”而是“object”;因为null虽然是单独的一个数据类型,但是它原本意思是空对象指针,浏览器使用typeof检测的时候会把它按照对象来检测 
2、使用typeof无法具体细分出到底是数组还是正则,因为返回的结果都是‘object’

 

typeof 12 =>'number'

var num = 13;

typeof num =>'number'

腾讯的面试题:

 

console.log(typeof typeof []);

typeof [] =>'object';

typeof 'object' =>'string';


Boolean()

把其他数据类型的值转换为布尔类型

只有0、NaN、空字符串、null、undefined这五个数据值转换为布尔类型的false,其余的都会变为true 
 

!单感叹号

!= :不等于 
感叹号在JS中还有一个作用:取反,先把值转换为布尔类型,然后再去取反 
 

!!两个

在一个感叹号取反的基础上取反,取两次反相当于没有做操作,但是却已经把其他类型值转换为布尔类型了,和Boolean是相同的效果 
 


字符串

在JS中单、双引号抱起来的都是字符串

 

12 ->number

'12' ->string

'[12,13]' ->string

常用方法: 
charAt charCodeAt 
substr substring slice 
toUpperCase toLowerCase 
indexOf lastIndexOf 
split 
replace 
match 
……


number数字

0 12 -12 12.5,JS中多增加了一个number类型的数据NaN 
typeof NaN ->”number’

NaN

not a number:不是一个数,但是属于number类型

NaN == NaN : false,NaN和任何其他值都不相等

isNaN

用来检测当前这个值是否是非有效数字,如果是非有效数字检测的结果是true,反之不是非有效数字则为false

 

isNaN(0) ->false

isNaN(NaN) ->true

isNaN("12") ->false 当我们使用isNaN检测值的时候,检测的值不是number类型的,浏览器会默认的把值先转换为number类型,然后再去检测

Number()

把其他数据类型值转化为number类型的值

 

Number('12') ->12

Number('12px') ->NaN 在使用Number转换的时候只要字符串中出现任何一个非有效数字字符,最后的结果都是NaN

Number(true) ->1

Number(false) ->0

Number(null) - >0

Number(undefined) ->NaN

Number([]) ->把引用数据类型转换为Number,首先需要吧引用数据类型转换为字符串(tostring),再把字符串转换为number即可 例如:[] ->'' ''->0

Number([12]) =>[12]->'12'->12

Number([12,13]) =>[12,13]->'12,13'->NaN

Number({name:'meng'} =>NaN

Number({}) =>NaN

parseInt()

也是把其他数据类型值转换为number,和number方法在处理字符串的时候有所区别

 

Number('12px') ->NaN

parseInt('12px') ->12

parseInt('12px13') ->12 提取规则:从左到右依次查找有效数字字符,直到遇见非有效数字字符为止(不管后面是否还有,都不找了),把找到的转换为数字

parseInt('px12') ->NaN

parseInt(12.5px) ->12

parseFloat()

在parseInt的基础上可以识别小数点

 

parseInt('12.5px') ->12

parseFloat('12.5px') ->12.5

课后思考:parseInt常用的只需要传递一个值做参数即可,但是它支持多个参数,回去后扩展其他参数的意思 例如:parseInt(‘12.5’,10)=>12


null和undefined

null:空,没有 
undefined:未定义,没有

”:空字符串,没有 
0:也可以理解为没有

空字符串和null的区别

都是去种树 
空字符串是挖了个坑,但是什么都还没有种下去 
null是连坑都没有挖

空字符串相对于null来说开辟了内存,消耗了那么一丢丢的性能

null和undefined的区别

null一般都是暂时没有,预期中以后是会有的(可能以后也没达到预期):在JS中null一般都是手动先赋值为null,后期我们再给他附具体的值

undefined:完全没在预料之内的

小明是个帅气的男孩子 
她现在的女朋友是null 
他的男朋友是undefined

symbol

给对象设置唯一的属性 
在vuex / redux中做行为派发的时候,统一管理派发的行为标识,标识的值可以是唯一值 
……

 

console.log(Symbol() === Symbol()); //=>false

let symb = Symbol('aa');

console.log(symb === symb); //=> true

Symbol.hasInstance 用于判断某对象是否为某构造器的实例

Symbol.toPrimitive 是一个内置的 Symbol 值,它是作为对象的函数值属性存在的,当一个对象转换为对应的原始值时,会调用此函数

Symbol.toStringTag

Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用

Symbol.isConcatSpreadable 用于配置某对象作为Array.prototype.concat()方法的参数时是否展开其数组元素。

Symbol.match 指定了匹配的是正则表达式而不是字符串

bigint

BigInt数据类型的目的是比Number数据类型支持的范围更大的整数值。在对大整数执行数学运算时,以任意精度表示整数的能力尤为重要。使用BigInt,整数溢出将不再是问题。(-9007199254740991 (-(2^53-1)) 和9007199254740991(2^53-1)之间的整数) 
要创建BigInt,只需在整数的末尾追加n即可 (9007199254740991n+1n =9007199254740992n )


对象数据类型 object

var obj = {name:’meng’,age:26,sex:’男’}; 
每一个对象都是由一到多组属性名(key键):属性值(value值)组成的,或者说有多组键值对组成的,每一组键值对中间用逗号分隔

属性:描述这个对象特点特征的

对象的属性名是字符串或者数字格式的,存储的属性值可以使任何的数据类型

对象名.属性名:忽略了 
对象名[属性名]:不能忽略单双引号

 

var obj = {name:'meng',age:26,sex:'男',friend:['哈哈', '呵呵'],0:100};

//=>获取某个属性名对应的属性值

obj.name

obj[name] obj['name']

//=>如果属性名是数字如何操作

obj.0 语法不支持

obj[0] / obj['0'] 两种都可以支持

//->如果操作的属性名在对象中不存在,获取的结果是undefined

obj.abc ->undefiend

//=>设置/修改:一个对象的属性名是不能重复的(唯一性),如果之前存在就是修改属性值的操作,反之不存在就是新设置属性的操作

obj.abc = 'come on';

obj['age'] = 27;

//=>删除

//->假删除:让其属性值赋值为null,但是属性还在对象中 obj.abc=null;

//->真删除:把整个属性都在对象中完全移除

delete obj.abc;

思考:obj[age]和obj[‘age’]有什么样的区别? 
age:变量名,代表的是它存储的值。 
‘age’:常量,字符串的具体值。

 

var age = 'name';

var obj ={

name:'mengxj',

age:8

};

console.log(obj.age); //=> 8

console.log(obj['age']);// =>8

console.log(obj[age]); //=>obj[age变量] =>obj['name'] =>获取name属性名的属性值 =>"mengxj"

obj[age]操作的是对象中的属性,并不是操作的变量,是操作的变量所存储的值,也就相当于obj[‘name’],而obj[‘age’]就是操作的是obj对象中age属性名,所以值就是age本身。

基本数据类型和引用数据类型的区别

JS是运行在浏览器种的(内核引擎),浏览器会为JS提供赖以生存的环境(提供给JS代码执行的环境)=> ‘全局作用域 window(global)’

 

var a = 12;

var b = a;

b = 13;

console.log(a);

var n = {name:'孟献军'};

var m = n;

m.name = mengxj;

console.log(n.name);

‘基本数据类型是按照值来操作的’:基本数据类型在赋值的时候,是直接吧值赋值给变量即可

‘引用数据类型是按照空间地址(引用地址)来操作’: 
var n = {name:’孟献军’};

1、先创建一个变量n 
2、浏览器首先会开辟一个新的存储空间(内存空间),目的是把对象中需要存储的内容(键值对)分别存储在这个空间中,为了方便后期找到这个空间,浏览器会给空间设定一个地址(16进制的) 
3、把空间的地址赋值给了变量


函数数据类型

函数数据类型也是按照引用地址来操作的

函数:具备一定功能的方法

 

//=>创建函数:相当于生产了一台洗衣机

function 函数名(){

//=>函数体:实现某一功能的具体JS代码

}

//=>执行函数:相当于使用洗衣机洗衣服(如果函数只创建了,但是没有去执行,函数没有任何的意义)

函数名();--->执行函数

 

function fn(){

console.log(1+1);

}

fn;=>输出函数本身

fn();=>执行函数(把函数体中实现功能的代码执行)

 

//=>形参:形式参数(变量),函数的入口

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

function fn(num1,num2){

console.log(num1+num2);

}

//=>实参:函数执行传递给函数的具体值就是实参

fn(10,20);


判断操作语句

if,else if,else

 

if(条件1){

//=>条件1成立执行的操作

}else if(条件2){

//=>条件1不成立,条件2成立,执行的操作

}

else{

//=>以上条件都不成立执行的操作

}

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

条件:

A==B,A!=B,A>=B,A<B

if(A){} 先把A转换为布尔类型,判断真假以此来决定条件是否成立

if(A>B && A<10){} 只有两个小条件都是真,整体条件才为真

if(A>B || A==0){} 只要其中一个小条件成立,整体条件就是真

BAT面试题:

 

var num = parseFloat('width:12.5px'); //=>NaN

if(num==12.5){ //=>开头说明一切

alert(12.5);

}else if(num==NaN){ //=>NaN不等于NaN

alert(NaN);

}else if(typeof num=='number'){

alert(0); //=>alert输出的内容是字符串

}else{

alert('啥也不是!');

}

//=>最终结果是'0'(字符串),因为alert是以字符串输出数据


三元运算符

条件?条件成立执行:条件不成立执行

if(条件){}else{}:三元运算符就是这种简单if、else的另外一种写法

 

var num = 10;

if(num>5 && num<=10){

num++; //=>num+=1 num=num+1 自身累加1

}else{

num--;

}

//=>改写成为三元运算符:如果条件成立或者不成立的某一种情况并不需要做什么处理,我们空着语法不符合,可以使用null、undefined、void 0(undefined)占位即可

num>5 && num<=10?num++:num--;

num>5 && num<=10?num++:;

 

var num = 10;

if(num>5 && num<=10){

num++;

console.log(num);

}

//=>改写成为三元运算符:某一种情况执行多条操作,使用下括号抱起来,中间用逗号分隔

num>5 && num<=10?(num++,console.log(num)):null;

 

var num = 10;

if(num>5 && num<=10){

num++;

break;/continue;/return;

}

//=>改写成为三元运算符:在三院运算符的操作中不能出现break,continue,return这些关键词,我们无法用三元运算符代替if,else

num>5 && num<=10?(num++,return):null;

 

var num = 10;

num>0?(num<10?num++:num--):(num>-10?num+=2:num-=2);

//=>改写成if else

if(num>0){

if(num<10){

num++;

}else{

num--;

}

}else{

if(num>-10){

num+=2;

}else{

num-=2;

}

}


switch case

switch case应用于if、else中一个变量在不同值情况下的不同操作

 

var num = 10;

switch(num){ //=>switch后面小括号中存放的事一个值(一般我们都写变量:把变量存储的值拿来用,有时候也可能是一个计算)

case 1:

……

break;

case 10: //=>case后面放入的都是值,目的是为了验证switch后面的值和哪一种case后面的值相等,相等的进行对应的处理

……

break; //=>每一种case结束后都要加break来结束当前的判断

default://=>switch后面的值和每一种case情况对应的值都不相等,执行最后的default,类似于else

……

}

案例剖析

 

var num = 10;

switch(num%2){ //=>先把取余操作进行运算,拿运算结果和case比较

case 0:

num++;

break;

case 2-1: //=>case后面也应该是值,此处先把2-1进行计算,把计算的结果和switch值比较

num--;

//=>最后一项可以不加break,不加也能跳出判断

default:

……

}

num%2:让num存储的值除以2取余数(0或1)

 

var num = 10;

switch(num%3){

case 0:

num++;//=>不加break,不管后面的条件是否成立,都会继续向下执行,直到遇到break为止

case 1:

num--;

break;

default:

num=0;

}

//=>小应用:把符合的值都去做同一件事,使用不加break实现

switch(num%3){

case 0:

case 1://=>余数是0或者1都执行减减的操作

num--;

break;

default:

num=0;

}

 

var num = 6;

switch(num%3){

case 0:

num++;

break;

case 1:

num--;

break;

default:

num=0;

}

num=>7

switch case中的比较使用的是:===

=:赋值,等号左边是变量,右边是值

==:比较,如果左右两边值的类型不一样,浏览器会默认转换为一样的然后再进行比较

‘6’==6 ‘6’->6 6=6=>true

===:绝对相等,不仅要求值一样,并且类型也要完全一样 ‘6’===6 =>false


循环操作语句

循环:重复做一件事情

for循环

 

for(设置循环起始值;设置循环执行的条件;步长累加){

//=>循环体:重复做的事情都在循环体中

}

 

//1、设置初始值

//2、验证条件

//3、条件成立,执行循环体,条件不成立,循环结束

//4、步长累加

for(var i=0;i<5;i++){

console.log(i);//=>0 1 2 3 4

}

console.log(i);//=>5

 

var i=0

for(;i<5;){

console.log(i);//=>没有步长累加,i永远都是0,循环条件永远成立“死循环”:项目中不能出现死循环,一旦出现,循环里面的事情都做不了

}

console.log(i);//=>5

 

for(var i=0;i<5;i+=2){

console.log(i);

continue;//=>结束本轮循环,继续执行下一轮,循环体中continue后面的代码都不会再执行,他会直接去执行步长,然后进入下一轮循环

console.log(i);//=>0

 

for(var i=0;i<5;i+=2){

console.log(i);

break;//=>结束整个循环:循 环体中一旦遇到break,首先后面的代码不执行了,而且步长累加也不执行了,循环都结束了

console.log(i);//=>0

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

2 5 10


自定义属性思想:

当我们在某一个阶段需要用到一些信息,但是此时不好获取这些信息,我们可以在之前好获取的时候,把这些后续需要用到的信息存储在元素对象的某一个自定义属性上,以后想用的时候直接在自定义属性上获取这些信息即可。

JS函数

函数是指一段在一起的、可以做某一件事儿的程序。也叫作子程序、(面向对象OOP中)方法。

函数是实现某一个功能的方法

创建函数

 

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

//=>【函数体】

//实现功能的具体js代码

}

执行函数

 

函数名(); //=》把创建的函数执行,而且这个函数可以执行很多次

函数名();

...

每一次执行都相当于把函数体中实现功能的JS代码重复执行了一遍

在真实项目中,我们一般都会把实现一个具体功能的代码封装在函数中: 
1、如果当前这个功能在页面中执行多次,不封装成为函数,每一次想实现这个功能,都需要重新把代码写一遍,浪费时间;而封装在一个函数中,以后想实现多次这个功能,我们就没有必要再重新写代码了,只需要把函数重新的执行即可,提高了开发效率; 
2、封装在一个函数中,页面中就基本上很难出现重复一样的代码了,减少了页面中代码的冗余度,提高了代码的重复利用率:‘低耦合高内聚’

我们把以上的特点称为函数封装(OOP面向对象编程思想,需要我们明白的就是类的继承、封装、多态)

JS中函数的核心原理

函数作为JS中引用数据类型中的一种,也是按照引用地址来操作的

 

function sum(){

var qiuhe = 1+1;

qiuhe*=20;

console.log(qiuhe.toFixed(2));

}

sum();

创建函数

  • 首先会在当前作用域中声明一个函数名(声明的函数名和使用var声明的变量名是一样的操作 var sum; function sum;这两个名字算重复了)

  • 浏览器首先会开辟一个新的内存空间(分配一个16进制地址),把函数体中写好的代码当做普通字符串存储在这个内存空间中(创建一个函数如果不执行,函数就没有意义)

  • 把内存空间的地址赋值给之前声明的那个函数名

函数执行

目的:把之前存储的实现具体功能的JS代码执行

函数执行的步骤

1、形成一个私有的作用域(AO私有变量对象等也都有了:存储当前作用域中声明的变量),然后进栈执行 
2、代码执行之前: 
+初始化作用域链 scope-chain <当前自己的私有作用域,函数的作用域(创建函数时候所在的作用域)> =>链的右侧时当前作用域的“上级作用域” 
+初始化this 
+初始化arguments 
+形参赋值:在当前作用域中声明一个形参变量,并且把传递的实参值赋值给它 
+变量提升 
3、代码执行 
4、出栈释放

私有变量

私有作用域下声明的变量 
形参是私有变量 
代码执行时候声明的变量

作用域链查找机制

私有作用域中代码执行,如果遇到一个变量,首先看是否为自己的“私有变量”,如果是“私有”的,则操作自己的,和外界没有必然的关系;如果不是自己私有的,则给予作用域链向其作用域中查找,看是否为上级作用域中私有的,如果也不是,则继续向上查找……一直找到全局作用域为止,我们把这种查找的过程,称为“作用域链查找机制

闭包

函数执行会形成一个私有的作用域,让里面的私有变量和外界互不影响(相互不干扰,外面的无法直接获取里面的变量值),此时我们可以理解为私有作用域把私有变量保护起来了,我们把这种保护机制称为闭包

栈内存

作用域(全局作用域/私有作用域):提供一个供JS代码执行的环境

堆内存

所有的引用数据类型,他们需要存储的内容都在堆内存中(相当于一个仓库,目的是存储信息)

  • 对象会把键值对存储起来

  • 函数会把代码当做字符串存储


函数中的形参和实参

形参:相当于生产洗衣机的时候提供的入口,需要用户执行函数的时候把需要的值传递进来,形参是个变量,用来存储和接收这些值 
实参:用户执行的时候传递给形参的具体值

 

//=>随便求出两个数的和

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 =>NaN 定义了形参但是执行的时候没有传递实参,默认实参的值是undefined

//=>如果有一个值没有传递的话,我们为了保证结果不是NaN,我们为其设置一个默认的值:0

if(num1 === undefined){

num=0;

};

if(typeof num2 === "undefined"){ //=>项目中常用的吧

num2 = 0;

}

//=>容错处理

arguments 实参集合

当我们不知道用户具体要传递几个值的时候(传递几个值都行),此时我们无法设置形参的个数;遇到此类需求,需要使用函数内置的实参集合:arguments 
1、arguments只有函数才有 
2、不管执行函数的时候是否传递实参,arguments天生就存在,没有传递实参ARG是个空的集合,传递了ARG中包含了所有传递的实参值 
3、不管是否设置了形参,ARG中始终存储了所有的实参信息

arguments是一个类数组集合 
1、以数字作为索引(属性名),从零开始 
arguments[0]第一个实参信息 
arguments[1]第二个实参信息 
arguments[n]第n+1个实参信息 
2、有一个length的属性,存储的是当前几个的长度(当前传递实参的个数) 
arguments.length 
arguments[‘length’]

arguments.callee:存储的是当前函数本身 
arguments.callee.caller:存储的是当前函数在哪执行的(宿主函数),在全局作用域下执行,结果是null

use strict:在JS代码执行前加入这句话:开启js的严格模式 
//=>arguments.call或arguments.callee .caller一般在真正项目中很少使用;因为在严的JS模式下不允许我们使用这两个属性,然后现有项目大部分都是基于严格模式来的

任意数求和(解析)

 

function sum(){

var total = null;

for(var i = 0; i<arguments.lenght; i++){

var cur = arguments[i];//=>每一轮循环获取当前传递的那个实数值

cur = Number(cur); //=>为了防止字符串+数字是字符串拼接不是数学的累加,我们最好把其他数据类型

//首先转换为Number类型

//=>为了防止传递的是非有效数字(数字+NaN=NaN),我们最好做一下非有效数字的检测:有效数字才进行累加

if(isNaN(cur) === flase){

total+=cur;

}

}

}

sum(10,20,30);

sum(10,20,'30');

任意数求和优化

 

function sum(){

var total = null;

for(var i = 0; i<arguments.length; i++){

var cur = Number(arguments[i]);

if(!isNan){

total+=cur;

}

// !isNaN?total+=cur:null;

}

}

sum(10,20,30,'40','meng')


JS中的返回值 return

返回值是函数提供的一个出口:如果我们想在外面使用函数私有的一些信息,那么就需要通过return,把这些信息返回出来供外面使用。

 

function sum(){

var total = null;

for(var i=0;i<arguments.length;i++){

var cur = Number(arguments[i]);

if(isNaN(cur)=== false){

total+=cur;

}

}

return total;//=>return后面跟着的都是值(返回的都是值):此处不是吧total变量返回,而是把total存储的值返回而已<=>return 60

}

console.log(sum(10,20,30));

//=>sum:代表的是函数本身

//=>sum():让函数先执行,代表的事当前函数返回的结果(return后面的是啥,相当于函数返回的是啥)

 

function sum(){

var total = 0;

return;

}

console.log(sum());//=>如果函数中没有写return或者return后面啥也没有,默认返回的结果就是undefined

 

function sum(){

var total = 0;

return;

console.log(total);//=>在函数体中遇到return后,return后面的代码都不再执行了

}

console.log(sum());

JS中的匿名函数

没有名字的函数

  • 函数表达式

  • 自执行函数

 

oBox.onclick = function(){

//=>把一个没有名字的函数(有名字也无所谓)作为值赋值给一个变量或者一个元素的某个事件等:函数表达式

}

自执行函数

 

;(function(n){

//=>创建函数和执行函数放在一起了,创建完成立马执行:自执行函数

//n形参 n=10;

})(10)

//=>以下都是自执行函数,符号只是控制语法规范

~function(n){}(10);

-function(n){}(10);

+function(n){}(10);

!function(n){}(10);


for in 循环

for in:用来遍历(循环)对象键值对的

 

var obj = {name:'孟',age:26,1:'献',3:'军',2:'哈哈'};

//=>var key; var attr;

//=>对象中有多少组键值对,我们的for in循环就遍历多少次(不一定)

//=>每一次循环key这个变量存储的都是循环这组键值对的属性名

//=>1、key存储的值都是字符串格式的(不管属性名是否为数字)

//=>2、在for in循环遍历的时候,大部分浏览器都是先把对象中的键值对进行排序(把数字属性名的按照数字大小排在前面),其次再把非数字的属性名按照之前编写的顺序排列,循环的时候按照重新排列的顺序依次遍历(小数算作字母不算作数字)

for(var key in obj){

console.log(typeof key); //->"string"

//key->属性名

console.log(obj.key);//->获取obj中key这个属性名对应的属性值 ->undefined

console.log(obj[key]);//->每一次循环把key变量存储的值(当前遍历的属性名)获取到放在中括号中,获取obj中对应属性的属性值

}

//'key':字符串key

//key:变量key,代表的是存储的值

//obj['key'] obj.key 一样的属性名都是key

//obj[key] 属性名不是key而是key变量存储的值


数据类型转换

把其他数据类型转换为number类型

isNaN、Number、parseInt、parseFloat 
在进行加减乘除数学运算的时候

 

true ->1 false ->0

'' ->0 '12' ->12 '12px' -> NaN/12 'MENG'->NaN

null ->0

undefined ->NaN

{} /^$/ function(){} ->NaN

[] ->''->0

[12]->'12'->12

[12,23]->'12,23'->NaN

//=>引用数据类型转换为数字

//=>通过toString方法把数组转换为字符串,然后在调用Number把字符串转换为数字

JS中的数学运算

+、-、*、/ 加减乘除 
除了加法有特殊性,其余的运算符都是数学运算,也就是遇到非数字类型,需要把其转换为number再进行运算

 

1-'1' ->0

10*null ->0

10/undefined ->NaN

10*[10] ->100

1+'1' ->11

null+'1' ->null1

//=>字符串拼接:是吧其他的值转换为字符串然后再拼接(toString)

//=>其他数据类型的toString是直接把值用单双引号包起来即可,只有对象的有特殊性,对象.toString()==='[object,object]'

将其他数据类型转换为布尔类型

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

}

在使用==进行比较的时候

在使用==进行比较的时候,如果左右两边数据类型不相同,浏览器会默认转换为相同的类型,然后再比较(===不会这样操作)

 

//=>对象和对象:比较的是空间地址,不是相同的空间,结果肯定是false

[] == [] ->false

var a = [];

var b = a;

a==b ->true;

//=>对象和数字:把对象转换为数字

[]==0 ->true

({})==NaN ->false NaN和自己不相等和其它任何值都不相等

//=>对象和字符串:把两边都转换为数字比较的

[]=='' ->true

//=>对象和布尔:把两边都转换为数字

[]==true ->false

[]==false ->0==0 ->true

![]==false ->![]把数组变为布尔再取反=false ->false==false ->true

//=>字符串和数字:字符串转换为数字

//=>字符串和布尔:都转为数字

//=>布尔和数字:布尔转换为数字

//=>规律:两个等于号比较,左右两边数据值的类型不一样,浏览器会把两边的类型都转换为数字然后再比较,但是null和undefined除外

null==undefined ->true

null===undefined ->false

null==0 ->false null以及undefined和其他任何值都不相等


数组的基础结构

数组也是对象数据类型的typeof[] ->'object'

数组也有属性名,只不过属性名是数字,我们把数字属性名称之为它的索引:数组是以数字作为索引,索引从零开始,有一个length属性代表数组的长度 
 

类数组:类似于数组,但是不是数组 
1、通过getElementsByTagName获取的元素集合是类数组 
2、函数中的实参集合arguments也是类数组 
  

循环数组中的每一项

 

var ary = [12,23,24];

Array.prototype.aa =100;

//=>for循环(项目使用更多)只能遍历到数组私有的一些属性,而for in循环可以把一些自定义的公共属性也能遍历到

//for 循环操作

for(var i=0;i<ary.lengtj;i++){

console.log(ary[i]);

}

//for in循环操作

for(key in ary){

console.log(ary[key]);

}

数组中的常用方法

数组中有很多常用的方法,console.dir(Array.prototype)(查看数组中的方法)

1、方法的意义和作用 
2、方法的形参 
3、方法的返回值 
4、通过此方法,原来的数组是否发生了改变

实现数组的增加,修改,删除

 

var ary = [12,23,24];

//=>增加

//1、push:向数组的末尾追加新内容

//参数:一到多个,任何数据类型都可以,想要给数据末尾追加什么,直接传递到push方法中即可,传递多个用逗号隔开

//返回值:新增后数组的长度

//原有数组改变了

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

//参数:需要追加的内容(可以是多个任何数据类型的值)

//返回值:新增后数组的长度

//原来数组改变了

//3、把数组当做一个普通的对象,使用对象键值对的操作,给其设置新的属性(索引)

//ary[ary.length]==xxx 向数组的末尾追加了新的内容

//=>删除

//1、pop:删除数组最后一项

//参数:无

//返回值:被删除的那一项内容

//原有数组改变了

//2、shift:删除数组第一项

//参数:无

//返回值:被删除那一项的内容

//原有数组改变了

//使用shift删除第一项之后,后面的每一项的索引都要向前进一位(导致后面项的索引发生改变)

//3、把数组当做普通的对象操作

//delete删除:`delete ary[索引]`删除指定索引这一项(当前项被删除后,原有数组其他项的索引不会改变;当前数组的length也不会改变)

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

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

//1、splice实现删除

//splice(n,m):从索引n开始删除m个(m不写是删除到数组的末尾)

//返回值:被删除的内容(以一个新数组保存)

//原有数组改变了

//->splice(0) 清空数组

//->splice() 一项都不删除,返回一个新的空数组,原数组不变

//->splice(0,1)删除第一项

//->splice(ary.length-1) 删除最后一项

//=>splice 实现修改

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

//splice实现增加

//splice(n,0,x):在修改的基础上,我们一项都不删除,把x插入到索引n的前面

//splice(0,0,x):向数组开头追加新的内容

//splice(ary.length,0,x)向数组末尾追加一个元素

数组的查询

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

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

2、将两个数组进行拼接 
concat:将多个数组拼接在一起 
参数:要拼接的内容(把内容放在原数组的后面),可以使一个数组,也可以是一些数据值 
返回值:拼接后的新数组 
原有数组不变 
->concat() 什么都没有拼接,相当于把原有数组克隆一份一模一样的新数组 
 
把数组转换为字符串 
1、toString:实现把数组转化为字符串(转换后的字符串以逗号分隔每一项) 
参数:无 
返回值:转换的字符串 
原有数组不变

2、join:把数组按照指定的分隔符转换为字符串,和字符串中的split相对应 
参数:指定的连接符 
返回值:转换后的字符串 
原有数组不变 
 

 

//=>已知数组中的每一项都是数字,想实现数组求和,我们如何实现?

// 1、循环实现

var total=null;

var ary = [12,23,24,25,26];

for(var i=0;i<ary.length;i++){

total+=ary[i];

}

// 2、利用join

var total = eval(ary.join('+')); //->eval:把字符串变为JS表达式执行

实现数组中每一项的排序和排列

 

//1、reverse:把数组中的每一项倒过来排列

//参数:无

//返回值:排列后的新数组

//原有数组改变

//2、sort:实现数组的排序

//参数:无或者回调函数

//返回值:排序后的新数组

//原有数组改变

//不传递参数的情况下:可以给10以内的数字进行升序排列,但是超过10的就无法处理了(多位数只识别第一位)

ary.sort(function(a,b){

return a-b; //=>升序

return b-a; //=>降序

})

验证数组中是否包含某一项

 

// indexOf / lastIndexOf:获取当前项在数组中第一次或者最后一次出现位置的索引

// 数组中的这两个方法在IE6~8下不兼容

// 字符串中的这两个方法兼容所有的浏览器

// 如果当前数组中并没有这一项,返回的索引是-1,我们根据这一点可以验证数组中是否包含这一项

if(ary.indexOf(12)>-1){

//->数组中包含12

}

`indexof兼容写法`

Array.prototype.myIndexOf = function myIndexOf(value){

var result = -1;

for(var i=0;this.length;i++){

if(value===this[i]){

result=i;

break;

}

}

return result;

}

ary.myIndexOf(12);

遍历数组中每一项的方法

 

//=>以下方法在IE6~8下都不兼容

// forEach:遍历数组中的每一项

ary.forEach(function(value,index){

//=>数组中有多少项,当前回调函数执行多少次;每一次传递进来的value就是当前遍历数组这一项的值,index就是遍历这一项的索引

});

// map:遍历数组中的每一项,在forEach的基础上,可以修改每一项的值

ary.forEach(function(value,index){

//=>数组中有多少项,当前回调函数执行多少次;每一次传递进来的value就是当前遍历数组这一项的值,index就是遍历这一项的索引

return xxx; //=>return后面返回的结果就是把当前遍历的这一项修改为xxx

});

数组去重

方案一: 
遍历数组中的每一项,拿每一项和他后面的项依次比较,如果相同了,则把相同的这一项在原来数组中删除即可

 

var ary = [1,2,2,2,3,1,3,3,4,4,5,2,3,6,5];

//=>ary.length-1:最后一项的后面没有内容了,我们不需要再比较

for(var i=0;i<ary.length-1;i++){

var cur = ary[i]; //=>当前遍历的这一项(索引i)

//=>把拿出的这一项和后面的每一项进行比较

//=>i+1:把当前项和它后面项比较,当前项索引是i,后一项索引是i+1

for(var j=i+1;j<ary.length;j++){

//ary[j]:作比较的那一项

if(cur === ary[j]){

//=>本次作比较的这一项和当前项相同,我们需要在原有数组中把作比较的这一项删除掉(作比较这一项的索引是j)

ary.splice(j,1); //=>数组塌陷问题:我们使用splice删除数组中的某一项后,删除这一项后面的每一项索引都要向前进一位(在原有索引上减一),此时如果我们j++,循环操作的值累加了,我们通过最新j获取的元素不是紧挨删除这一项的元素,而是跳过一项获取的元素

j--; //=>先让j--,然后再j++,相当于没加没减,此时j还是原有索引

}

}

}

console.log(ary);

 

var ary = [1,2,2,2,3,1,3,3,4,4,5];

for(var i=0;i<ary.length-1;i++){

var cur;

cur = ary[i];

for(var j=i+1;j<ary.length;){

cur===ary[j]?ary.splice(j,1):j++;

}

}

console.log(ary);

方案二: 
利用indexof来验证当前数组中是否包含某一项,包含把当前项删除掉(不兼容IE6~8)

 

var ary = [1,2,2,2,3,1,3,3,4,4,5];

for(var i=0;i<ary.length-1;i++){

var cry = ary[i]; //=>当前项

var curNextAry = ary.slice(i+1);//=>把当前项后面的那些值以一个新数组返回,我们需要比较的就是后面的这些项对应的新数组

if(curNextAry.indexOf(cry)>-1){

//=>后面项组成的数组中包含当前这一项(当前这一项是重复的),我们把当前这一项删除掉即可

ary.splice(i,1);

i--;

}

}

console.log(ary);

方案三: 
遍历数组中的每一项,把每一项作为新对象的属性名和属性值存储起来,例如:当前项1,对象中存储的{1:1} 
{1:1,2:2,2:2}(对象中不允许存在属性名相同键值对) 
在每一次向对象中存储之前,首先看一下原有对象中是否包含了这个属性(’typeof obj[xxx]===’undefined’说明当前对象中没有xxx这个属性’),如果已经存在这个属性说明数组中的当前项是重复的(1、在原有数组中删除这一项 2、不再向对象中存储这个结果),如果不存在(把当前项作为对象的属性名和属性值存储进去即可)

 

var ary = [1,2,2,2,3,3,4,2,3,5,4,2,5];

var obj = {};

for(var i=0;i<ary.length;i++){

var cur = ary[i];

if(typeof obj[cur] !== 'undefined'){

//=>对象中已经存在改属性:证明当前项是数组中的重复项

ary.splice(i,1);

i--;

continue;

}

obj[cur] = cur; //=>obj[1]=1 {1:1} 存储

}

//=>使用splice会导致后面的索引项前进一位,如果后面有很多项,消耗的性能很大

//=>思路:我们把最后一项拿过来替换当前要删除的这一项,然后再把最后一项删除

for(var i=0;i<ary.length;i++){

var cur = ary[i];

if(typeof obj[cur] !=== 'undefined'){

ary[i] = ary[ary.length-1]; //把最后一项存储在当前项

ary.length--; //数组长度发生改变

i--; //数组索引发生改变

continue;

}

obj[cur] = cur; //=>obj[1]=1 {1:1} 存储

}

//=>最终优化版:

var ary = [1,2,2,2,3,3,4,2,3,5,4,2,5];

Array.prototype.myUnique = function myUnique(){

var obj={};

for(var i = 0;i<this.length;i++){

var item = this[i];

if(typeof obj[item] !== 'undefined'){

this[i] = this[this.length-1];

this.length--;

i--;

continue;

}

obj[item] = item;

}

obj = null;

return this;

}

console.log(ary.myUnique().sort(function (a,b){

return a-b;

}))


代码注释习惯

 

// bubble:冒泡排序

// @parameter (参数)

// ary:[array] 需要实现排序的数组

// @return

// [array] 排序后的数组(升序)

// by team on 2017/10/19

冒泡排序

原理:让数组中的当前项和后一项进行比较,如果当前项大于后一项,我们让两者交换位置(小->大) 
每一轮从前到后两两比较,虽然不一定实现最后的排序效果,但是可以把当前最大的放在末尾 
具体比较的轮数:ary.length-1 数组有多长,我们只需要把总长度-1个数分别放在末尾,即可实现最后的排序

第一轮比较5次:一共留个,不需要和自己比较 
第二轮比较4次:一共六个,不用和自己比,也不用和第一轮放在末尾的那个最大值比 
第三轮比较3次 
…… 
每一轮比较的次数 ary.length-1(不用和自己比) 当前已经执行的轮数(执行一轮向末尾放一个最大值,这些值不需要再比较)

a=12 b=13 a和b交换值 
c=a a=b b=c 
a=a+b; =>25 b=a-b; =>12 a=a-b =>13

 

var ary = [1,2,5,4,9,7,6,3,11,15,18,13];

function bubble(ary){

//->外层循环控制的是比较的轮数

for (var i =0; i< ary.length - 1; i++){

//->里层循环控制每一轮比较的次数

for(var j=0;j<ary.length-1-i;j++){

// ary[j]:当前本次拿出来这一项

// ary[j+1]:当前项的后一项

if(ary[j] > ary[j + 1]){

//-> 当前这一项比后一项还要大,我们让两者交换位置

var temp = ary[j];

ary[j] = ary[j+1];

ary[j+1] = temp;

}

}

}

return ary;

}

递归

函数自己调用自己

面试题:1~100之间,把所有能被3并且能被5整除的获取到,然后累加求和 
方案一

 

var total = null;

for(var i = 0;i<=100;i++){

if(i % 3 === 0 && i % 5 === 0){

total+=i;

}

}

console.log(total);

方案二 递归

 

function fn(){

if(num > 100){

return 0;

}

if(num%15=0){

retrun num + fn(num + 1);

}

return fn(num + 1);

}

console.log(fn(1));

1-> fn(2)

2-> fn(3)

...

15-> 15+fn(16)

16-> 15+fn(17)

....

30-> 15+fn(30)

-> 15+30+fn(31)

....

15+30+45+60+75+90+0

需求:1~10以内的所有偶数乘积

 

function fn(num){

if(num<=1){

return 1;

};

if(num % 2 === 0){

return num*fn(num - 1);

}

return fn(num - 1);

}

var result = fn(10);


快速排序

原理

 

var ary = [11,13,15,16,12,14];

function quick(ary){

//-> 如果传递进来的数组只有一项或者是空的,我们则不再继续取中间项拆分

if(ary.length <= 1){

return ary;

}

//-> 获取中间项的索引:把中间项的值获取到,在原有数组中删除中间项

var centerIndex = Math.floor(ary.length / 2),

centerValue = ary.splice(centerIndex,1)[0];//->splice返回的是个数组,数组中包含了删除的那个内容

//->用剩余数组中的每一项和中间项进行比较,比中间项大的放在右边,比他小的放在左边(左右两边都是新数组)

var aryLeft = [],

aryRight = [];

for(var i = 0; i < ary.length; i++){

var cur = ary[i];

if(cur < centerValue){

aryLeft.push(cur);

}else{

aryRight.push(cur);

}

//->cur < centerValue?aryLeft.push(cur) : aryRight.push(cur);

}

return quick(aryLeft).concat(centerValue, quick(aryRight));

}

console.log(ary);

插入排序

 

var ary = [12,15,14,13,16,11];

function insert(ary){

//->先抓一张牌(一般都抓第一张)

var handAry = [];

handAry.push(ary[0]);

//-> 依次循环抓取后的牌

for(var i = 1; i < ary.length; i++){

var item = ary[i];//->本次新抓的这张牌

//->那新抓的牌和手里现有的牌比较

//->j = handAry.length - 1 从手里的最后一张牌开始比较

for(vr j = handAry.length - 1; j>=0; j--){

//handAry[j]:当前比较的手里的这张牌

//->新抓的牌比当前比较的这张牌大了,我们把新抓的牌放在它的后面

if(item > handAry[j]){

handAry.splice(j + 1,0,item);

break;

}

if(j === 0){

//->新抓的牌是最小的,我们把新抓的牌放在最开始的位置

handAry.unshift(item);

}

}

}

retrun handAry;

}

console.log(insert(ary));


Math中常用的方法

数学函数:但是他是对象数据类型的 typeof math ->’object’ 
Math对象中给我们提供了很多常用操作数字的方法 
console.dir(Math) 可以查看Math的所有方法

abs:取绝对值

 

Math.abs(12) ->12

Math.abc(-12) ->12

ceil && floor

Math.ceil:向上取整 
Math.floor:向下取整

 

Math.ceil(12) ->12

Math.ceil(12.1) ->13

Math.ceil(12.9) ->13

Math.ceil(-12.1) ->-12

Math.ceil(-12.9) ->-12

Math.floor(12) ->12

Math.floor(12.1) ->12

Math.floor(12.9) ->12

Math.floor(-12.1) ->13

Math.floor(-12.9) ->13

round

Math.round:四舍五入

 

Math.round(12.3) ->12

Math.round(12.5) ->13 正数中5包含在向上

Math.round(-12.3) ->-12

Math.round(-12.5) ->-13 负数中5包含在向下

random

Math.random:获取[0,1)之间的随机小数

 

for(var i=0;i<100;i++){

console.log(Math.random());

}

//=> 需求:获取[0,10]之间的随机整数

Math.round(Math.random()*10)

//=> 需求:获取[3,15]之间的随机整数

Math.round(Math.random()*12+3)

获取[n,m]之间的随机整数 
Math.round(Math.random()*(m-n)+n)

max && min

Math.max:获取一组值中的最大值 
Math.min:获取一组值中的最小值

 

Math.max(12,32,56,16)-> 56

Math.min(12,23,24,26,35)-> 12

PI

Math.PI:获取圆周率(π)

 

Math.PI ->3.141592653

pow && sqrt

Math.pow:获取一个值的多少次幂 
Math.sqrt:开平方

 

Math.pow(10,2) -> 100

Math.sqrt(100) -> 10


字符串中常用的方法

在JS中用单(双)引号包裹起来的都是字符串

 

var str = 'good good study,day day up!';

//=>字符串就是由零到多个字符组成的

// 特点一:

// 第一个字符索引0

// 第二个字符索引1

// ……

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

// 特点二

// 有一个叫作length的属性,存储的是当前字符串中字符的个数(字符串的长度)

str[0] ->'w' 第一个字符

str.length ->46

str[str.length-1] -> '!' 最后一个字符

str[100] -> undefined 如果指定的索引不存在获取的结果是undefined

真实项目中,我们经常操作字符串,此时我们需要掌握常用的一些字符串操作方法 
console.dir(String.prototype)

charAt && charCodeAt

str.charAt(索引):返回指定索引位置的字符,和str[索引]的区别在于,当指定的索引不存在的时候,中括号的方式获取的是undefined,而charAt获取的是空字符串

str.charCodeAt(索引):在charAt基础上,把获取的字符变为Unicode编码值(对应ASCII码表) 
48~57: 0-9 
65~90:A-Z 
97~122:a-z 
……

String.fromCharCode(十进制的Unicode值):把值按照ASCII码表中的信息,转换为原有字符,和charCodeAt正好对应 
 

substr && substring && slice

实现字符串截取的三个办法 
str.substr(n,m):从索引n开始,截取m个字符 
str.substring(n,m):从索引n开始,截取到索引为m处(不包含m),把找到的部分截取 
str.slice(n,m):和substring语法一样,区别在于slice支持以负数做索引 
  
当索引是负数的时候,浏览器在处理的时候,使用字符串的总长度加上负数索引,然后按照正数处理操作

细节: 
1、如果只传递了n(str.substr(n) / str.substring(n)),相当于从索引n开始一直截取到字符串的末尾 
2、如果传递的索引超出最大限制,也是把能截取的部分截取掉即可 
3、如果一个参数都不传递:相当于把整个字符串都截取(字符串的克隆) 
…… 
 

toUpperCase && toLowerCase

toUpperCase:把字母全局大写 
toLowerCase:把字母全局小写 
 

indexOf && lastIndexOf

str.indexOf:获取当前字符在字符串中第一次出现位置的索引 
str.lastIndexOf:获取的是最后一次出现位置的索引 
 

如果当前字符在字符串中没有出现过,结果是-1;我们根据这个规律可以验证一下当前字符串中是否包含某个字符

 

if(str.indexOf('?') === -1){

//=>没有出现过

}

if(str.indexOf('?')>=0){

//=>出现过

}

split

str.split:按照某一个字符把字符串拆分成数组中的某一项,和数组中的join方法是对应的 
 

replace

str.replace:实现字符的替换 
执行一次replace只能替换一次,如果有好几个都需要替换,在不使用正则的情况下我们需要执行很多次replace 
 

有些需求即时执行很多次replace也实现不了,此时需要使用正则处理,真实项目中replace一般都是和正则搭配使用的 
 

trim && trimLeft && trimRight

str.trim:除去字符串首尾的空格 
str.trimLeft:去除字符串开始的空格 
str.trimRight:去除字符串结尾的空格


案例:queryURLParameter

获取地址栏中URL地址?好传递的参数值 
node_百度搜索

javascript_百度搜索

目标:把问号传递的参数值分别的解析出来 
obj={wd:’javascript’,rsv_spt:1,issp:1}

方案一

 

var str = 'https://www.baidu.com/s?wd=javascript&rsv_spt=1&issp=1';

//=>{wd:'javascript',rsv_spt:1,issp:1}

var questionIndex = str.indexOf('?'); //获取?出现位置的索引

str = str.substring(questionIndex + 1); //从?后的第一个字符开始截取

//->console.log(str) => 'wd=javascript&rsv_spt=1&issp=1'

var ary = str.split('&');

//console.log(ary) => ['wd=javascript','rsv_spt=1','issp=1']

var obj = {};

for(var i=0;i<ary.length;i++){

var cur = ary[i]; //->['wd=javascript']

var curArry = cur.split('='), //->以=拆分循环出['wd',javascript]

key = curArry[0];

value = curArry[1];

obj[key] = value;

}

console.log(obj);

方案二

 

function queryURLParameter(url){

//=>URL:传递的参数(我们当前要解析的URL地址)

var quesIndex = url.indexOf('?'),

obj = {};

if(quesIndex === -1){ //->url中没有问号传参:直接返回空对象

return obj;

}

url = url.substring(quesIndex + 1);

var ary = url.split('&');

for(var i=0;i<ary.length;i++){

var curAry = ary[i].split('=');

obj[curAry[0]] = curAry[1];

}

return obj;

}

console.log(queryURLParameter('https://www.baidu.com/s?wd=javascript&rsv_spt=1&issp=1'));

方案三

使用正则实现功能

 

var str = 'https://www.baidu.com/s?wd=javascript&rsv_spt=1&issp=1'

String.prototype.myQueryURLParameter = function myQueryURLParameter(){

var obj = {},

reg = /([^=?&]+)=([^=?&]+)/g;

this.replace(reg, function(){

var arg = arguments;

obj[arg[1]] = arg[2];

});

return obj;

}

console.log(str.myQueryURLParameter());


验证码

真实项目中的验证码: 
真实项目中验证码一般都是后台处理的,后台返回给客户端展示的是一个图片(图片中包含了验证码)

  • 字母+数字

  • 问答

  • 12306 选择图片

  • 成语填空

  • 图片拼图

  • 滑动拖拽 
    ……

 

var codeBox = document.getElementById('codeBox');

//=>生成四五随机验证码:取值范围

var areaStr = '0123456789qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM';

//->62位(0~61)

var result = '';

for(var i=0;i<4;i++){

//=>随机获取一个0~61之间的整数:作为接下来获取字符的索引

var ran = Math.round(Math.random()*(61-0)+0);

//=> 根据索引获取一个随机字符

var char = areaStr.charAt(ran);

//=> 把每一次循环获取的字符存放在最后结果中

result+=char;

}

codeBox.innerHTML = result;


对验证码进行封装

 

var codeBox = document.getElementById('codeBox');

//=>queryCode:获取思维验证码

function queryCode(){

var result = '',

areaStr = '0123456789qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM';

for(var i=0;i<4;i++){

var ran = Math.round(Math.random()*(61-0)+0);

var char = areaStr.charAt(ran);

//验证一下新获取的char字符是否已经在result中存在了,如果存在了我们不存储,重新在获取一遍,反之才累加到result中

//=>去重:result.toLowerCase().indexOf(char.toLowerCase())统一转换为小写字母

if(result.toLowerCase().indexOf(char.toLowerCase())>-1){

i--;

continue;

}

result+=char;

}

codeBox.innerHTML = result;

}

//=> 加载页面需要执行一次这个方法:生成四位验证码

queryCode();

//=>点击盒子重新生成四位二维码 (此处不加下括号:这块只是再把函数绑定给元素的点击事件,方法还没有执行,点击的时候才执行)

codeBox.onclick = queryCode;


获取DOM元素的方法

  • document.getElementById 一个元素对象

  • [context].getElementByTagName 元素集合

  • [context].getElementClassName 元素集合

  • document.getElementByName 节点集合

  • document.documentElement 获取整个HTML对象

  • document.body 获取整个BODY对象

  • document.head 获取整个HEAD对象

  • [context].querySelector 一个元素对象

  • [context].querySelectorAll 获取元素集合

  • ……

getElementById

此方法的上下文只能是document 
一个HTML页面中元素的ID理论上是不能重复的

1、如果页面中的ID重复了,我们获取的结果是第一个ID对应的元素对象

2、在IE7及更低版本浏览器中,会把表单元素的name值当做id来识别使用(项目中尽量不要让表单的name和其他元素的id相同)

3、如果我们把js放在结构的下面,我们可以直接使用id值来获取这个元素(不需要通过getElementById获取),而且这种方式会把页面中所有id是他的元素都获取到(元素对象 / 元素集合) => 不推荐

 

//=>获取页面中id值为#box1的所有元素标签

var allList = document.getElementByTagName('*'),

result = [];

for(var i=0;i<allList.lenght;i++){

var item = allList[i];

item.id === 'box1'?result.push(item):null;

}

comsole.log(result);

getElementsByTagName

上下文是可以自己来指定 
获取到的结果是一个元素集合(类数组集合)

1、获取的结果是集合,哪怕集合中只有一项,我们想要操作的是这一项(元素对象),需要先从集合中获取出来,然后再操作

 

<body>

<div></div>

<div></div>

<div></div>

</body>

<script>

var bodyBox = document.getElementsByTagName('body');

bodyBox.getElementsByTagName('div');//->Uncaught TypeError:bodyBox.getElementsByTagName is not a function 此时的bodyBox是一个类数组集合,我们需要使用的事其中的第一项,而不是整个集合

bodyBox[0].getElementsByTagName('div')

</script>

2、在指定的上下文中,获取所有子子孙孙元素中标签名叫作这个的(后代筛选)

 

<div id='box'>

<ul>

<li></li>

……

</ul>

<div>

<ul>

<li></li>

……

</ul>

</div>

……

</div>

<script>

box.getElementsByTagName('li');

box.getElementsByTagName('div');

</script>


getElementsByClassName

上下文也可以随意指定 
获取的结果也是一个元素集合(类数组集合)

1、真实项目中我们经常会通过样式类名来获取元素,getElementsByClassName这个方法在IE6~8中是不兼容的

 

function byClass(strClass, context){

context = context || document;

if(isHighVersion) return [].slice.call(context.getElementsByClassName(strClass));

strClass = strClass.replace(/^\s+|\s+$/g, '').split(/\s+/g);

var tagList context.getElementsByTagName('*'),

result = [];

for(var i=0;i<tagList.length;i++){

var item = tagList[i],

itemClass = item.className;

var isMatch = true;

for(var k = 0; k < strClass.length; k++){

var reg = new RegExp('(^| +)' + strClass[k] + '( +|&)');

if(!ret.test(itemClass)){

isMatch = false;

break;

}

}

isMatch ? result.push(item) : null;

}

return result;

}

getElementsByName

通过元素的NAME属性值获取一组元素(类数组:节点集合NodeList) 
它的上下文也只能是document

1、IE浏览器只能识别表单的name属性值,所以我们这个方法一般都是用来操作表单元素的

document.documentElement / documnt.body

获取HTML或者BODY(一个元素对象)

 

document.documentElement.clienWidth || document.body.clienWidth //=>获取当前浏览器窗口可视区域的宽度

//=>clientHeight 是获取高度

querySelector / querySelectorAll

在IE6~8下不兼容,而且也没什么特别好的处理他的兼容,所以这两个方法一般多用于移动端开发使用

querySelector:获取一个元素对象 
querySelectorAll:获取的是 一个元素集合

只要是CSS支持的选择器,这里大部分都支持

 

document.querySelectorAll('#box1'); //获取ID为box1的元素对象

document.querySelectorAll('.box'); //获取类名为box的元素对象

document.querySelectorAll('div'); //获取文档中所有的div标签元素集合

document.querySelectorAll('body>div');//获取body元素瞎的所有div标签元素集合

document.querySelectorAll('#box2 li');//获取id为box2下所有的li标签元素集合

DOM的节点

node:节点,浏览器认为在一个HTML页面中所有内容都是节点(包括标签、注释、文字文本等)

  • 元素节点:HTML标签

  • 文本节点:文字内容(高版本浏览器会把空格和换行也当做文本节点)

  • 注释节点

  • document文档节点、

  • ……

元素节点

nodeType:1 
nodeName:大写标签名(在部分浏览器的怪异模式下,我们写的标签名是小写,他获取的就是小写,反之……) 
nodeValue:null

[curEle].tagName:获取当前元素的标签名(获取的标签名一般都是大写)

文本节点

nodeType: 3 
nodeName: #text 
nodeValue:文本内容

注释节点

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

文档节点

nodeType:9 
nodeName:#document 
nodeValue:null

节点是用来描述页面中每一部分之间关系的,只要我们可以获取页面中的一个节点,那么我们就可以通过相关的属性和方法获取页面中所有的节点 
 

childNodes

获取当前元素所有的子节点(节点集合:类数组) 
注:不仅仅是元素子节点,文本,注释等都会包含在内;子节点说明只是在儿子辈分中查找:

children

获取所有元素子节点(元素集合) 
在IE6~8下获取的结果和标准浏览器中有区别(IE6~8中会把注释节点当做元素节点获取到)

parentNode

获取当前元素的父节点(元素对象)

previousSibling / nextSibling

previousSibling:获取当前节点的上一个哥哥节点(不一定是元素节点也可能是文本或注释) 
nextSibling:获取当前节点的下一个弟弟节点

previousElementSibling / nextElementSibling

previousElementSibling:获取当前节点的上一个哥哥元素节点 
nextElementSibling:获取当前节点的下一个弟弟元素节点 
IE6~8下不兼容

firstChild / lastChild

firstChild:当前元素所有子节点中的第一个(也不一定是元素节点,可能是文本和注释) 
lastChild:当前元素所有子节点中的最后一个

firstElementChild lastElementChild (IE6~8不兼容)

DOM的增删改

真实项目中,我们偶尔会在JS中动态创建一些HTML标签,然后把其增加到页面中

document.createElement

在JS中动态创建一个HTML标签

appendChild

容器.appendChild(新元素) 
把当前创建的新元素添加到容器的末尾位置

insertBefore

容器.insertBefore(新元素,老元素) 
在当前容器中,把新创建的元素增加到老元素之前

真实项目中很多需求都是通过动态创建元素来完成的,其中有一个需求:解析一个URL地址每一部分的信息(包含问号传递的参数值) 
1、纯字符串拆分截取 
2、编写强大的正则,捕获到需要的结果 
3、通过动态创建一个A标签,利用A标签的一些内置属性来分别获取每一部分的内容 
……

 

var link = document.createElement('a');

link.href = 'http://www.zhufengpeixun.cn/stu/?name=zxt&age=27#teacher'

//->此处地址就是我们需要解析的URL

/*

* hash:存储的事哈希值 ‘#teacher’

* hostname:存储的是域名 ‘www.zhufengpeixun.cn’

* pathname:路径 ‘/stu/’

* protocol:协议'http:'

* search:存储的是问号传递的参数值,没有传递是空字符串‘?name=zxt&age-27’

*/

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;

}

removeChild

容器.removeChild(元素) 
在当前容器中把某一个元素移除掉

replaceChild

容器.replaceChild(新元素,老元素) 
在当前容器中,拿新元素替换老元素

cloneNode

元素.cloneNode(false/true) 
把原有的元素克隆一份一模一样的,false:只克隆当前元素本算,true:深度克隆,把当前元素本身以及元素的所有后代都进行克隆

[set/get/remove]Attribute

给当前元素设置/获取/移除 属性的(一般操作的都是他的自定义属性) 
box.setAttribute(‘myindex’,0) 
box.getAttribute(‘myindex’) 
box.removeAttribute(‘myindex’)

使用xxx.index=0 和 xxx.setAttribute(‘index’,0) 这两种设置自定义属性的区别? 
xxx.index:是把当前操作的元素当作一个普通对象,为其设置一个属性名(和页面中的HTML标签没关系) 
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结构存在映射关系的(设置的自定义属性可以呈现在结构上)

previousSibling 获取当前元素的上一个哥哥节点,兼容写法

首先获取当前元素的上一个哥哥节点,判断当前获取的节点是否为元素节点(nodeType===1),如果不是,基于当前获取的节点,找他的上一个哥哥节点……(找几次不知道)一直到找到的节点是元素节点为止 
如果在查找过程中,发现没有上一个哥哥节点了(找到头了),则不在继续查找 
使用 while 循环 while(条件){循环体}

 

function prev(curEle){

var p = curEle.previousSibling;

while(p && p.nodeType !==1){

p.curEle.previousSibling;

}

return p;

}


Date日期操作基础讲解

Date是日期类,通过他可以对时间进行处理

 

var time = new Date(); //->获取当前客户端本机时间(当前获取的时间不能作为重要的参考依据)

//->获取是的结果是一个日期格式的对象:Tue Mar 30 2021 00:46:17 GMT+0800 (中国标准时间)

//->typeof new Date() ->'object'

//->time.getFullyear() 获取四位整数年份

//->time.getMonth() 获取月份(0~11代表1~12月)

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

//=>时间格式的字符串

'2021-03-30' (IE下识别不了)

'2021/03/30'

'2021/03/30 16:15:34'

1617036377914 (如果传递的是距离1970年的那个毫秒差,也是可以识别转换的,但是只能是数字,不能是字符串)

倒计时考试

 

var timeBox = document.getElementById('timeBox');

function computer() {

var nowTime = new Date(), //->当前时间

targeTime = new Date('2021-03-30 01:57:00'); //最终时间

var spanTime = targeTime - nowTime; //->获取的是两个时间之间的毫秒差

// 已到达时间我们就可以提醒开始考试了

if (spanTime <= 0) {

timeBox.innerHTML = '开始考试';

window.clearInterval(timeBox);

return;

}

//还未到达开始时间,在总的毫秒差中计算出时,分,秒

var hour = Math.floor(spanTime / (1000 * 60 * 60)); // 计算时

spanTime -= hour * 1000 * 60 * 60; //把小时占据的毫秒去除掉,剩下的计算分,秒

var minute = Math.floor(spanTime / (1000 * 60));

spanTime -= minute * 1000 * 60; //把分钟占据的毫秒去除掉,剩下的计算秒

var sucond = Math.floor(spanTime / 1000);

//给个位时间前加0

hour<10?hour='0'+hour:null;

minute<10?minute='0'+minute:null;

sucond<10?sucond='0'+sucond:null;

// 将获取到的时,分,秒拼接

timeBox.innerHTML = hour + ':' + minute + ':' + sucond;

}

computer();

//每一秒钟都重新执行computer();

window.setInterval(computer,1000);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值