前言:
学好前端,从夯实原生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,隐式全局变量
数据类型:
基本数据类型:Number、String、Boolean、Undefined、Null、Symbol(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的引用
解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。
结束语:
这次的笔记就先整理到这里啦,后面会不定期更新哒~