前提
JavaScript是脚本语言,是一种运行在浏览器中的解释型的编程语言。
1、浏览器分成两部份:渲染引擎和JS引擎(解析器)。
前者解析html和css,后者读取js代码,执行代码时逐行解释每一句源码(转换为机器语言)如chrome的v8。
2、JS的组成:ES、dom和bom
ES:规定基础语法。
DOM:文档对象模型,通过其提供的接口可以对页面上各种元素进行操作。
BOM:浏览器对象模型, 提供了独立于内容的,可与浏览器窗口进行互动的对象结构。(如弹出窗、。控制浏览器跳转、获取分辨率等。)
书写位置
- 行内式的js:直接写到元素内部,如οnclick=“alert(‘aa’)”
- 内嵌式的js: head内部写入
<script></script>
,中间写入就JS代码- 外部的js:
<script src="my.js"></script>
注意:中间不要写代码,适用于js代码比较多的情况- 位置不同,解析先后会有所不同。
相关链接: JS放在文件头部还是尾部.
关于浏览器渲染
相关链接: 浏览器渲染原理.
- 服务器端Response响应后,浏览器端拿到代码后,在内存条中开辟出一块栈内存,给代码执行提供环境。同时分配一个主线程去一行行解析执行代码。(进栈执行,执行完就出栈)
- 当浏览器遇到link/script/img等请求后,都会开辟新的线程去加载资源文件
- 自上而下,解析html生成DOM树
- 构建Render树:接下来不管是内联式,外联式还是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另外一棵用于渲染的树——渲染树(Render tree)
- 布局Render树:计算在设备视口(viewport)内的确切位置和大小,这个计算阶段叫回流(Layout)
- 绘制Render树 - 最后遍历渲染树并用UI后端层将每一个节点绘制出来
其中,可做性能优化:减少HTTP的请求次数和大小(比如把所有css合成为一个)
- 资源合并压缩
- 图片懒加载
- 音视频走流文件
等方法
基础知识
- 输入框:prompt(’ ‘);
弹出提示框:alert(’ ‘);
控制台输出:console.log(’ '); - 变量的使用 var声明
特殊情况:只声明不赋值——结果为undefined;不声明不赋值——报错;不声明直接赋值——可以但不建议 - 数据类型:js的变量数据类型只有在数据运行中,根据等号右边的值来确定。js是动态语言,变量的数据类型可以变化。
若有变量打算存储为对象(Object),但暂时没想好存啥,可以给null
如 var aa=null;
原始数据类型 | 栈内存 |
---|---|
数字型 Number类型 | 整数、小数点、八进制和十六进制(程序会转化成十进制)等,NaN非数字:isNaN()用来判断非数字,是数字返回false |
字符串型String类型 | 一般推荐使用单引号,转义符:\n换行,\t tab缩进 |
布尔型Boolean | true当1,false当0 |
Undefined | 未定义(注:undefined和数字相加结果为NaN) |
Null | 空值 |
引用数据类型 | 堆内存 |
---|---|
对象 | |
数组 | |
函数 |
①原始数据类型:
直接存储在栈(stack)中的简单数据段,占据空间小、大小比较稳定。
属于被频繁使用数据,所以放入栈中存储;
②引用数据类型:
存储在堆(heap)中的对象,占据空间大、大小不固定。
在栈内存中只是存了一个地址来表示对堆内存中的引用。
堆内存是无序存储,可以根据引用直接获取。
引用数据类型进行复制时,系统也会自动为新的变量在栈内存中分配一个值,但这个值仅仅是一个地址。
也就是说,复制出来的变量和原有的变量具有相同的地址值,指向堆内存中的同一个对象。
栈区(stack):编译器自动分配释放,存放函数的参数值,局部变量的值 等,操作方式类似于数据结构的栈。
堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。
当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。所以,JavaScript中对引用数据类型的操作都是操作对象的引用而不是实际的对象。可以理解为,栈内存中保存了一个地址,这个地址和堆内存中的实际值是相关联的。
- typeof():获取变量数据类型
- 数据类型转换:
转换成字符串型:var str=num.toString(); 或 console.log(String(num));
转换成数字型:parseInt( ‘78’ ); parseFloat(‘3.14’); Number(‘123’); 隐式转换(利用算术运算- */):console.log(‘123’ - 1);
转换成布尔型:console.log(Boolean(‘123’));除了‘ ’、NaN、null、undefined外其他都返回为true - 编译器和解释器的区别:
前者在执行代码前进行编译(相当于做菜),后者在运行时及时解释并立即执行(相当于吃火锅) - 标识符(取的名)不能是关键字(如if、for、break、default等)和保留字(如const、public等 )
- ++a 和 a++:单独使用时效果一样;否则++a自加1再赋值,a++先赋值再自加1
- 逻辑与短路运算(表达式1 && 表达式2):如果表达式1结果为真,则返回表达式2;若为假,则返回表达式2.
逻辑或短路运算(表达式1 || 表达式2):如果表达式1结果为真,则返回表达式1;反之亦然。 - 流程控制
① if…else if…else: 只会有一个代码块被执行,一旦代码块执行了,则直接结束语句
② switch(expression){
case judgement1:…break;
case judgement2:…break;
…
default:…break;
} //数据类型值要一样才能匹配上
③ while(条件表达式){} //先判断条件表达式,若为true则执行循环体;
④ do{ }while(条件表达式); //先执行一次循环,再判断条件表达式
⑤三元表达式: 条件表达式?表达式1:表达式2
⑥continue关键字:立即跳出本次循环,进入下一循环。
break:立即跳出整个循环。
数 组
数组 | 操作 |
---|---|
创建数组 | var a=new Array();var a=[ ‘xiao’,‘xiaohong’] |
添加参数 | arr.concat( 9,[10,13]);这个方法会先创建当前数组的副本,然后将接收到的参数添加到这个副本的末尾 |
判断类型 | Array.isArray(arr); 或者 console.log(arr instanceof Array) |
数组变字符串 | arr.join( ); arr.join( ’ 、’) ; arr.toString( ); |
翻转 | arry.reverse( ) |
排序 | console.log(‘数组排序-降序:’ +arr.sort(function(a, b) { …return b - a})); |
数组截取和替换 | '截取从索引值3开始到最后:arr.slice(3)); 截取从索引值1开始获取三个元素:arr.slice(1, 3)); 截取获取最后2位:arr.slice(-2)); 从索引值1开始的两个元素替换成aaa:arr.slice(1, 2, ‘aaa’); |
索引位置 | indexOf/lastIndexOf索引位置,没有则返回-1 |
数组遍历 | //every遍历数组:arr.every(function(a, b, c) {…return true; }); 规则:c[b] = a; //forEach方法(无返回值):arr.forEach(function(x, index, a){ });//map遍历数组(返回数组):var arr2 = arr.map(function(item){return item*item;}); |
filter过滤器 | var newArr=arr.filter ( function(element,index,arr){ return …;}); 返回满足过滤条件组成的数组 |
函 数
- 声明函数:
①function fn( ){ }
②ES6中方式:( )=>{ }
③函数表达式(匿名函数),将函数存到变量/数组里(调用时要写括号,且调用要放在声明的后面): var func = function( ) { }; - 调用:
① fn();
②在事件中调用,直接写变量名,不使用括号,如:document.οnclick=fn; - 参数
形参:形式上的参数——给函数声明一个参数;
实参:实际的参数——在函数调用时给形参赋的值
function func(形参1,形参2){
//函数执行代码
}
func(实参1,实参2);//调用时传参
- arguments的使用
当不确定有多少个参数时用arguments来获取,它是当前函数的一个内置对象,所有函数内都内置了一个arguments对象,该对象中存储了传递的所有实参。
- arguments展示形式是伪数组:
- 具有数组的length属性
- 按索引方式存储
- 没有真正数组的一些方法(如pop() 、 push())
- 可按数组方式遍历arguments
只有函数才有arguments对象,且每个函数内都内置了这个arguments对象。可以和 rest 联合使用。
如:function aaa(a,b, …rest){ }
- 作用域
全局变量:在全局作用域下的变量; 如果在函数内部,没有声明直接赋值的变量也叫全局变量。
局部变量:在局部作用域(函数)下的变量,形参也可以看作局部变量。
全局变量只有浏览器关闭的时候才销毁,比较占内存资源;
局部变量当我们程序执行完毕时就会销毁,比较节约内存资源。
ES6中新增了块级作用域,使用let声明的变量只能在块级作用域里访问,有“暂时性死区”的特性(也就是说声明前不可用)。
块作用域由 { } 包括,if语句和for语句里面的{ }也属于块作用域。
var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
- JS实际上只有一个全局作用域,若变量在全局作用域内都没查找到,会报错
RefrenceError
规范
由于所有全局变量都绑定在window上,如果不同的js文件,使用了相同的全局变量,会产生冲突=>如何减少冲突?
//唯一全局变量(为了不直接绑定在window上)
var a = {};
//定义全局变量
a.name = '华华';
a.add = function(x,y){
return x+ y;
}
- ES6 的 let 关键字能解决局部作用域冲突问题,建议用 let 去定义局部作用域的变量
- 作用域链:内部函数访问外部函数的变量,采用的是链式查找方式决定取哪个值。(就近原则)
预解析
- JS解析器分两步:预解析和代码执行
- 预解析:js引擎会把js里面所有的var以及function提到当前作用域的最前面。
预解析分为变量预解析(变量提升)和函数预解析(函数提升)。
(1)变量预解析:所有的变量声明提升到当前作用域的最前面,不提升赋值操作。
(2)函数预解析:所有的函数声明提升到当前作用域的最前面,不调用函数。 - 代码执行:按照代码书写顺序从上至下执行
对 象
- 对象:是一组无序的相关属性和方法的集合。
- 创建对象:
①利用对象字面量: (方法冒号跟的是一个匿名函数)
var obj={
uname:'zhangsan',
age:18,
sayhi:function(){
console.log('方法');
}
};
②利用new Object:(等号赋值 的方法添加对象的属性和方法)
var obj=new Object( ); //创建空对象
obj.name='zhangsan';
obj.age=18;
obj.sayhi=function(){
console.log('方法');
};
③利用构造函数:
前面两种方式一次只能创建一个对象,而构造函数里面封装的是对象.
构造函数不需要return就可以返回结果
调用构造函数必须使用new(该过程叫对象实例化)
格式: function 构造函数名(){
this.属性=值;
this.方法=function();
}
var aa= new 构造函数名();
④利用原型prototype对象
- 原型链
相关链接: 原型和原型链.//这是一个构造函数 function Foo(name,age){ this.name=name; this.age=age; } /*根据要点3,所有的函数都有一个prototype属性,这个属性是一个对象 再根据要点1,所有的对象可以自由扩展属性 于是就有了以下写法*/ Foo.prototype={ // prototype对象里面又有其他的属性 showName:function(){ console.log("I'm "+this.name);//this是什么要看执行的时候谁调用了这个函数 }, showAge:function(){ console.log("And I'm "+this.age);//this是什么要看执行的时候谁调用了这个函数 } } var fn=new Foo('小明',19) /*当试图得到一个对象的属性时,如果这个对象本身不存在这个属性,那么就会去它 构造函数的'prototype'属性中去找*/ fn.showName(); //I'm 小明 fn.showAge(); //And I'm 19
使用原型链,我们只需要在构造函数中给属性赋值就行了,方法可以写在prototype中,可以节省很多资源。
- 使用对象
- 调用对象的属性采用 对象名 . 属性名 或者 对象名[‘属性名’]
- 调用对象的方法 对象名.方法名( ) 不要忘记小括号
`
- 遍历对象: for…in… 和 for…of…
for(var k in obj){ //k是属性名(在数组中是索引)
console.log(obj[k]); //得到的是属性值
console.log(k);
}
for(var a of arr){
console.log(a); //a是数组里的值
}
var a=new Map([['tomcat',123],['lease',456],['sad',789]]);
for(let i of a){
console.log(typeof(i)); //结果为object
}
-
内置对象:JS语言自带的对象
- Math数学对象:不是构造函数,不需要new来调用,可以直接使用
Math.PI:圆周率吧
Math.abs( ):绝对值方法/隐式转换
Math.max( )
Math.floor( ):向下取整,往最小了取值
Math.ceil( ):向上取整
Math.round( ):四舍五入,.5特殊,往大了取
Math.random( ):返回了一个随机的小数,方法内无参数,[ 0,1 ) - Date( )日期对象,是一个构造函数,必须使用new
① 获取当前时间直接new;
② 返回参数内时间日期格式字符串new Date(‘2022-2-2 18:00:00’)或new Date(‘2022/2/2’);
③ getTime( ) 属性、valueOf( ) 属性、+Date( ) 和 Date.now( ) 获取到距离1970.1.1总的毫秒数。 - Array数组对象,是一个构造函数
var arr=new Array(2); //这个2表示数组的长度为2
var arr=new Array(2,3); //等价于 [2,3]
- Math数学对象:不是构造函数,不需要new来调用,可以直接使用
-
JSON
是一个轻量级的数据交换格式,就是一个规范,能提升网络传输效率
格式:
对象:{ }
数组:[ ]
所有键值对都用 key: value
//对象转换为json字符串
var jsonu= JSON.stringify(user);
//json字符串转化为对象, 参数为json字符串
var obj = JSON.parse("{"name":"qingling","age":17}")
json 和 js 对象的区别:
var obj = {a:"hello", b:"hellob"};
var json = "{"a":"hello","b":"hellob"}";
//注:json内部只能用双引号