原生js手记(一):

258 阅读17分钟

前言:

学好前端,从夯实原生js基础开始!这是一份自己的学习笔记,并不适合每个人,如果你觉得适合你,可以点赞收藏一下哦~(1-4)

js是什么?

(1)是一门脚本语言

(2)是一门解释性语言

(3)是一门动态类型的语言

(4)是一门基于对象的语言

(5)是一门弱类型的语言

js分为三个部分:

(1) ECMAScript 标准 ----------js基本的语法

(2) DOM --------------------文档对象模型

(3) BOM ---------------------浏览器对象模型

基本概念:

区分大小写:

变量函数名操作符 都是区分大小写的~

标识符:

定义:就是指变量函数属性的名字、或者函数的参数

规则:

(1)第一个字符必须是字母下划线、或者美元符号($);

(2)其他字符可以是字母、下划线、$或者数字;

(3)多个单词的情况下采用驼峰命名法;

(4)不能使用关键字;

关键字:

变量:

变量名的注意问题:

  • 变量的名字要有意义;
  • 变量名有一定的规范:一般以字母,下划线,$符号开头;
  • 变量名一般都是小写的;
  • 变量名如果是多个单词,用驼峰命名法
  • 不能使用关键字
  • 不会的单词用拼音,也要用驼峰命名法

注意1:只定义但是未进行初始化的变量,会保存一个特殊的值,undefined

var a;
console.log(a)  //undefined

注意2:在一个函数里定义一个变量,有var,就是局部变量,函数调用完,该变量就会被销毁,若没有var,就是隐式全局变量,在函数外部的任何地方都可以访问到

function test () {
    var name = 'mengyun';
}
test();
alert(name);    //错误,因为是局部变量
function test () {
    name = 'mengyun';
}
test();
alert(name);    //mengyun,隐式全局变量

数据类型:

基本数据类型:NumberStringBooleanUndefinedNullSymbol(ES6新增)

复杂数据类型:Object

typeof 操作符:

(1)如何获取变量的数据类型是什么?

用 typeof 变量名 或者 typeof(变量名)

(2)typeof 的结果可能有哪些值:

undefined、boolean、string、number、object、function (typeof 数组名 ,得到的也是object)

注意:typeof null 的结果是object,是因为null被认为是一个空对象的引用

注意:只要意在保存对象的变量还没有真正保存对象,就应该明确的让该变量保存null值。这样做不仅可以体现null作为空对象指针的惯例,还可以进一步区分null 和 undefined。

注意:undefined 值是派生自null值的,所以 console.log(null == undefined); 的结果是true

Number类型:

(1)不要用小数去验证小数( 0.1 + 0.2 != 0.3 )

是因为在进制转换和进阶运算的过程中出现精度损失。(因为浮点数值的最高精度是17位小数,但是在进行算术计算时其精度远远不如整数)

(2)不要用NaN验证是不是NaN

console.log(NaN == NaN)     //false

如何验证这个结果是不是NaN,应该使用isNaN(),(可以翻译为不是一个数字吗?)若括号内是数字,则是false,不是数字,则是true

console.log(isNaN(NaN))     //true
console.log(isNaN(10))      //false
console.log(isNaN('10'))    //false(可以被转换成数值10)
console.log(isNaN('blue')) //true(不可以被转换成数值)
console.log(isNaN(true)) //true(可以被转换成1)

(3)如果一个变量的结果是undefined,和一个数字进行计算,结果是NaN

var num;
console.log(num+10);    //NaN

数值转换:

有三个函数可以把非数值转换为数值:Number()、parseInt()、parseFloat();

(1) Number():转数字(比较严格)

console.log(Number(true))   //1
console.log(Number(false))   //0
console.log(Number(null))   //0
console.log(Number(undefined))   //NaN
console.log(Number("123"))   //123
console.log(Number("012"))   //12
console.log(Number("1.2"))   //1.2
console.log(Number("hello"))   //NaN
console.log(Number(""))   //0

Number()在转换字符串的时候比较复杂且不够合理,所以我们更多的使用parseInt()

(2)parseInt():转整数

console.log(parseInt("123bl"))      //123
console.log(parseInt(""))      //NaN
console.log(parseInt("bl12"))      //NaN
console.log(parseInt("22.54"))      //22

(3)parseFloat():转小数

console.log(parseFloat("123b"))     //123
console.log(parseFloat("0xA"))     //0,十六进制的始终会转换为0
console.log(parseFloat("22.35"))     //22.35
console.log(parseFloat("032.35"))     //32.35

String类型:

字符串具有不可变性

字符串的长度如何获取: str.length

var str = "hello";
str[1] = 'w';
console.log(str);   //hello
console.log(str.length);   //5 

字符串拼接:

var str1='10';
var str2=20;
console.log(str1+str2); //1020

只要有一个是字符串,+就是拼接,而不是相加,-就是相减,不是拼接,也就是隐式转换,浏览器会把str1先转换成数字类型,再跟str2做运算

也就是说,要把某个数字转换为字符串,也可以使用 + ,num+"",即可

(1).toString()

var num =10;
console.log(num.toString());

(2)String()

console.log(String(10));    //"10"
console.log(String(true));    //"true"
console.log(String(null));    //"null"
console.log(String(undefined));    //"undefined"

区别:如果变量有意义,调用.toString()转换,否则会报错 如果变量没有意义,调用String()转换

var num;
console.log(num.toString());    //报错
var num=null;
console.log(num.toString());    //报错
var num;
console.log(String(num));    //undefined
var num=null;
console.log(String(num));    //null

其他类型转布尔类型:

console.log(Boolean(null))  //false
console.log(Boolean(undefined))  //false
console.log(Boolean(1))  //true

Object类型:

创建对象:

var obj = new Object()  //法1
var obj2 = {}           //法2

目前对象先写这么多~

操作符:

算术运算符:+ - * / %

一元运算符:++ --    (只需要一个操作数) i++ 先运算后加一 ++i 先加一后运算


二元运算符:+ - * / %

三元运算符:表达式1?表达式2:表达式3

复合运算符:+= -= *= /= %=

关系运算符: > < >= <= == != === !== (关系运算表达式的结果是布尔类型)

逻辑运算符:&& || !

赋值运算符: =

运算符的优先级:从高到低

()
一元运算符  ++ -- !
算术运算符 先 * / % 后 + -
关系运算符 > >= < <=
相等运算符 == != === !==
逻辑运算符 先&& 后||
赋值运算符  =

js中可以表示哪些进制?

var num=10; //十进制
var num = 012 //八进制
var num=0x123 //十六进制

二进制:遇到2进一

八进制:遇到8进一
00000001
00000002
00000003
00000004
00000005
00000006
00000007
00000010  ------8

十进制:遇到10进一

十六进制:遇到f进一
00000001
00000002
00000003
00000004
00000005
00000006
00000007
00000008
00000009
0000000a
0000000b
0000000c
0000000d
0000000e
0000000f
00000010 -----16

交换变量:三种方法

法1:借助第三个变量
var a=1;
var b=2;
var t;
var t=a;
a=b;
b=t;
法2:加法
var a=1;
var b=2;
var s=a+b;
a=s-a;    //2
b=s-b;    //1
法3:^ 按位异或 如果m、n两个值不相同,则异或结果为1。如果m、n两个值相同,异或结果为0。(转成二进制进行对比)
var a=1;
var b=2;
a=a^b;
b=a^b;  //1
a=a^b;  //2

流程控制的三种方式:

顺序结构

分支结构 if-else switch case

循环结构 while do-while for for-in

练习计算闰年?(四年一闰,百年不闰,四百年再闰)

isPN:function(year) {
    if (year % 100 == 0 && year % 400 == 0){
        return true;
    }
    if (year % 100 != 0 && year % 4 == 0){
        return true;
    }
    return false;
},

isPN(2018);//调用传值
switch(表达式){
    case 值1:代码1;break;
    case 值2:代码2;break;
    case 值3:代码3;break;
    case 值4:代码4;break;
    case 值5:代码5;break;
    .
    .
    .
    default:代码6
}

注意:switch-case 语句中和case后面的值比较的时候是严格模式,也就是全等操作符

什么时候用if-else if-else ... :一般是对范围的判断,比如分数;

什么时候用switch-case语句:一般是对具体的值的判断,比如成绩的等级

循环:

while循环:

while(循环的条件){
    循环体;
    计数器++;
}

do-while循环:

do{
    循环体;
    计数器++;
}while(条件)

区别:do-while至少执行一次循环体,而while可能一次不执行

for 循环:

画星星案例:

for(let i=0;i<5;i++){//控制行数 
    for(let j=0;j<=i;j++){
        document.write("☆");
    }
    document.write("<br />")
}

for-in 语句: MDN的for-in

是一种精准的迭代语句,可以用来枚举对象的属性

for(let index in array) {  
    console.log(index,array[index]);  
};  

说到循环,这里想对循环遍历做个总结: 在平时的工作中,我们无非是对数组、对象、字符串进行遍历的操作

给定一个数组:var array = [1,2,3,4,5,6,7];

(1)第一反应:for循环(妈呀,我真是个菜鸡)

for (var i = 0; i < array.length; i) {  
    console.log(i,array[i]);  
}  

(2)for-in

不仅可以对数组,还可以对对象

for(let index in array) {  
    console.log(index,array[index]);  
};  
var A = {a:1,b:2,c:3,d:"hello world"};  
for(let k in A) {  
    console.log(k,A[k]);  //a-1 b-2 c-3 d-hello world
} 

提示:for...in不应该用于迭代一个 Array,其中索引顺序很重要。

(3) forEach

array.forEach(function(currentValue, index, arr), thisValue)

currentValue 必需。当前元素

index 可选。当前元素的索引值。

arr 可选。当前元素所属的数组对象。

thisValue 可选。传递给函数的值一般用 "this" 值。如果这个参数为空, "undefined" 会传递给 "this" 值

array.forEach((curr,index,arr)=>{
    console.log(curr,index,arr);
})

(4)在es6中,for-of

for(let v of array) {  
    console.log(v);  //1 2 3 4 5 6 7
};  
let s = "helloabc"; 
for(let c of s) {  
    console.log(c); 
}

总结来说:for-in总是得到对像的key或数组,字符串的下标,而for-of和forEach一样,是直接得到值

参考链接

break关键字:

如果在循环中使用,则立刻跳出当前所在的循环;

for(var i=0;i<5;i++){
    while(true){
        console.log('哈哈');
        break;
    }
}

//打印出5次哈哈,只能跳出当前所在的while循环,并不能跳出for循环

continue关键字:

在循环中如果遇到continue关键字,直接开始下一次循环

var i=0;
while(i<10){
    console.log('哈哈');
    continue;
    i++;
} 

//死循环,永远不会执行i++,所以一直循环

数组:

一组有序的数据

数组的作用:可以一次存储多个数据

1、通过字面量的方式创建数组

var 数组名 = [];

2、通过构造函数创建数组

var 数组名 = new Array()    //new 创建  Array()  构造函数

var arr = new Array(长度)
var arr = new Array(5)
console.log(arr)    //[undefined*5]

如果数组中没有数据,但是有长度,数组中的每个值就是undefined

总:1:无论是构造函数的方式还是字面量的方式,定义的数组,如果有长度,那么每个元素默认是undefined

总结2:构造函数的方式创建数组的时候,如果在Array(一个数字),表示数组的长度(数组元素的个数),如果在Array(多个值);这个数组中就有数据了,数组的长度就是这些数据的个数

数组的索引不仅可以获取值,还可以设置值

  • 设置 : 数组名[索引]=值
  • 获取 : 数组名[索引]

求数组中的最大值的5种方法:

遍历:
var arr = [10,40,20,30,50];
var max = arr[0];
for(let k of arr){
    max=Math.max(max,k)
}
console.log(max);
es6展开:
var arr = [10,40,20,30,50];
console.log(Math.max(...arr));
apply传参数:
var arr = [10,40,20,30,50];
console.log(Math.max.apply(null, arr))
排序比较:
var arr = [6, 4, 1, 8, 2, 11, 23];
arr.sort(function(a,b){return a - b;});
console.log(arr[arr.length - 1])
reduce比较:
var arr = [6, 4, 1, 8, 2, 11, 23];
console.log(arr.reduce((prev,next)=>{
    return Math.max(prev, next);
}));

反转数组:

var arr = [1,2,3,4,5,6];
var arr1 = [];
for(i=arr.length-1;i>=0;i--){
    arr1[arr1.length]=arr[i]
}
arr = arr1;
console.log(arr);
var arr = [1,2,3,4,5,6];
arr.reverse();
console.log(arr);

冒泡排序:(把所有的数据按照一定的顺序排列,从小到大或者从打到小)

从小到大:
var arr = [9,3, 1, 4, 2, 7, 8, 6, 5];
//外层循环控制比较的轮数
for(i=0;i<arr.length-1;i++){
    //内层循环控制每一轮比较的次数
    for(j=0;j<arr.length-1-i;j++){
        if(arr[j]>arr[j+1]){
            var t = arr[j];
            arr[j]=arr[j+1];
            arr[j+1]=t; 
        } 
    }
    console.log(arr+'=========='+(i+1)+'轮');
}

结果:
3,1,4,2,7,8,6,5,9==========1轮
1,3,2,4,7,6,5,8,9==========2轮
1,2,3,4,6,5,7,8,9==========3轮
1,2,3,4,5,6,7,8,9==========4轮
1,2,3,4,5,6,7,8,9==========5轮
.
.
1,2,3,4,5,6,7,8,9==========8轮

优化后:很明显,第四轮就排序好了,那么我们可以设置一个排好序的标志

var arr = [9,3, 1, 4, 2, 7, 8, 6, 5];
let flag=true;
for(let i=0;i<arr.length-1;i++){//控制比较的轮数
    for(let j=0;j<arr.length-i-1;j++){
        flag=true;
        if(arr[j]>arr[j+1]){
            t=arr[j];
            arr[j]=arr[j+1];
            arr[j+1]=t;
            flag=false;
        }
    }
    if(flag){
        break;
    }
    console.log(arr+'=========='+(i+1)+'轮');
}

结果:
3,1,4,2,7,8,6,5,9==========1轮
1,3,2,4,7,6,5,8,9==========2轮
1,2,3,4,6,5,7,8,9==========3轮
1,2,3,4,5,6,7,8,9==========4轮

函数:

把一大堆重复的代码封装,在需要的时候直接调用即可;

函数的作用:代码的重用;

函数的定义:

function 函数的名字(){
     函数体
 }

函数的调用:

函数名();

函数的注意问题:

  • 函数需要先定义,然后才能使用;

  • 函数的名字:驼峰命名法;

  • 函数不能重名,否则后面的覆盖前面的

函数参数:在函数定义的时候,函数名字后面的小括号里的变量就是参数,目的是函数在调用的时候,用户传进来的值操作

形参:函数在定义的时候,小括号里的变量叫形参

实参:函数在调用的时候,小括号里传入的值叫实参,实参可以是变量,也可以是值

函数的返回值:在函数内部有return关键字,并且在关键字后面有内容,这个内容被返回了, 当函数调用之后,需要这个返回值,那么就定义变量接收,即可

如果一个函数中有return,那么这个函数就有返回值,如果一个函数没有返回值,但是在调用的时候接收了,那么结果就是undefined

return后面的内容是不会执行的

若函数调用时,f1(),这个括号不写,输出的的f1函数的代码

arguments(对象伪数组):

arguments对象的长度是由传入的参数的个数决定的,不是由定义函数时的命名参数的个数决定的。

函数的定义方式:

命名函数:函数如果有名字,就是命名函数

匿名函数:函数如果没有名字,就是匿名函数(匿名函数不能直接调用)

函数声明:重名会覆盖
function f1() {
    console.log("哈哈哈");
}
f1();               //嘎嘎啊
function f1() {
   console.log("嘎嘎啊");
}
f1();               //嘎嘎啊
var 变量=function(){}

像这种,把一个函数给一个表达式,此时形成了函数表达式,

如果是函数表达式,那么此时前面的变量中存储的就是一个函数,而这个变量就相当于是一个函数,就可以直接加小括号调用了
函数表达式:
var f2 = function() {
    console.log("哈哈哈");
 };
 f2();               //哈哈哈
 f2 = function() {
    console.log("嘎嘎啊");
 };
 f2();               //嘎嘎啊

注意:函数表达式后面,赋值结束后,要加分号

函数的自调用,没有名字,调用-----声明的同时,直接调用
一次性的-------安全
(function(){
    console.log("menegyun")
 })()

(1)函数也是一种数据类型:

function f1(){
    console.log(11);
}
console.log(typeof f1)  //function

(2)函数可以作为参数使用,如果一个函数作为参数,那么我们说这个参数(函数)可以叫回调函数。只要看到了一个函数作为参数使用了,那就是回调函数

function f1(fn){
    fn();
}
function f2(){
    console.log("哦,这也可以");
}
f1(f2);

(3)函数是可以作为返回值使用的

function f1(){
    console.log("f1函数调用了");
    return function () {
        console.log("haha");
    }
}
因为有返回值,所以要有个变量接收
var ff=f1();   //调用,此时ff就是一个函数了
ff();   //haha

作用域:

全局变量:声明的变量是使用var声明的,那么这个变量就是全局变量,全局变量可以在页面的任何位置使用;

局部变量:在函数内部定义的变量,是局部变量,外面不能使用

隐式全局变量:声明的变量没有var,就叫隐式全局变量

除了函数以外,其他的任何位置定义的变量都是全局变量

全局变量,如果页面不关闭,那么就不会释放,就会占空间,消耗内存;全局变量是不能被删除的,隐式全局变量是可以被删除的

全局作用域:全局变量的使用范围

局部作用域:局部变量的使用范围

块级作用域:一对大括号就可以看成是一块,在这个区域中定义的变量,只能在这个区域中使用,但是js中没有块级作用域,只有函数除外

var i=0;
while(i<5){
    var num = 1;
    i++;
}
console.log(num)    //1,可以访问,可见没有块级作用域
function f1(){
    num=100;
}
f1();
console.log(num);    //100,因为是隐式全局变量,一般来说函数调用完成,局部变量就销毁了

作用域链:

var num = 10;
function f1(){
    var num=20;
    function f2(){
        var num=30;
        function f3(){
            var num=40;
            console.log(num);   //40
        }
        f3();
    }
    f2();
}
f1();

若f3里面没有num,向上找f2,num打印出来是30,再没有,再往上找,以此类推,这样的关系就叫作作用域链

预解析:

提前解析代码

console.log(num);   //没有报错,undefined,相当于把变量的声明提前了
var num=10;

即.....

var num;
console.log(num);
num=10;
f1(); 
function f1() {
   console.log("haha");     //haha,没有报错,把函数的声明提前了
} 

即.....

function f1() {
   console.log("haha");     //haha,没有报错,把函数的声明提前了
} 
f1(); 

总结:预解析做什么事?把变量的声明提前了,提前到当前所在的作用域的最上面(也就是使用之前);函数的声明也会被提前,提前到当前所在的作用域的最上面(也就是调用之前);函数中的变量只会提前到函数的作用域中的最前面,不会出去

f1();
function f1(){
    console.log(num);   //undefined
    var num=10;
}
f1();
var num=20;
function f1(){
    console.log(num);   //undefined,由于作用域链的原因,先在当前作用域找num
    var num=10;
}
f1();
var num=20;
function f1(){
    console.log(num);   //undefined
}

即.....

function f1(){
    console.log(num); 
}
f1();
var num=20;

即.....

var num;
function f1(){
    console.log(num); 
}
f1();
num=20;
console.log(a);     //函数的代码
function a(){
    console.log('haha')
}
var a=1;
console.log(a);     //1

即.....

var a;
function a(){
    console.log('haha')
}
console.log(a);     //函数的代码
a=1;
console.log(a);     //1

在变量和函数都提升的时候,变量在函数的上面
f1();
console.log(c);     //9
console.log(b);     //9
console.log(a);     //报错
function f1() {
    var a=b=c=9;        //(因为相当于var a;a=9;b=9;c=9;b和c相当于隐式全局变量)
    console.log(a);     //9
    console.log(b);     //9
    console.log(c);     //9
}

但是,当把一个匿名函数赋值给一个 变量,成为函数表达式之后,跟命名函数有差别

f1();      //报错(变量的提升)
var f1=function(){
    console.log(a);     
    var a=10;
}

相当于...

var f1;
f1();
f1=function(){
    console.log(a);     
    var a=10;
}

垃圾收集:

JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。

原理:找出那些不再继续使用的变量,然后释放其占用的内存。

一旦数据不再有用,最好通过将其值设置为null来释放其引用,这就叫做解除引用

function f1(name){
    var obj = {};
    obj.name=name;
    return obj;
}
var res = f1("mengyun");
res=null;   //手工解除res的引用

解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

结束语:

这次的笔记就先整理到这里啦,后面会不定期更新哒~