1-JavaScript应该掌握的基础概念
发布于 2022年 04月 09日 18:44
JavaScript
三部分组成:
- ECMAScript(ES):描述了该语言的语法和基本对象
- DOM:文档对象模型,描述处理网页内容的方法和接口
- BOM:浏览器对象模型,描述与浏览器进行交互的方法和接口
推荐书籍:(阅读顺序-最上面的最重要) 1.JavaScript高级程序设计 2.ES6 3.你不知道的JavaScript 4.JavaScript权威指南
变量和数据类型(variable && data type)
- 【变量】
- 多种定义方式:var/let/const/function/import/class
- 严谨的命名规范:区分大小写/驼峰命名/关键字保留字
- 【数据类型】
- 基本数据类型(值类型):数字number、字符串string、布尔值boolean、null、undefined
- 引用数据类型:object(数组、对象、正则……)、function
- Symbol:唯一值
数据类型详细解读
【运行和输出】 运行JS代码:浏览器或者NODE 输出方式:alert/confirm/prompt/console.log/dir/table 【数据类型详解和相互转换】 数字:NaN/isNaN/parseInt/parseFloat…… 布尔:!/!! 字符串:字符串拼接 null/undefined object对象:键值对
常用的浏览器及内核
- webkit内核(V8引擎)
- 谷歌Chrome
- Safari
- Opera >=V14
- 国产浏览器
- 手机浏览器
- ...
- Gecko
- 火狐Firefox
- Presto
- Opera <V14
- Trident
- IE
- IE EDGE开始采用双内核(其中包含chrome迷你)
谷歌浏览器的控制台(F12/Fn+F12)
- Elements:查看结构样式,可以修改这些内容
- Console:查看输出结果和报错信息,是JS调试的利器
- Sources:查看项目源码
- Network:查看当前网站所有资源的请求信息(包括和服务器传输的HTTP报文信息)、加载时间等(根据加载时间进行项目优化)
- Application:查看当前网站的数据存储和资源文件(可以盗图)
JS做客户端语言
按照相关的JS语法,去操作页面中的元素,有时还要操作浏览器里面的一些功能
- ECMAScript3/5/6...:JS的语法规范(变量、数据类型、操作语句等等)
- DOM(document object model):文档对象模型,提供一些JS的属性和方法,用来操作页面中的DOM元素
- BOM(browser object model):浏览器对象模型,提供一些JS的属性和方法,用来操作浏览器的
JS中的变量 variable
变量:可变的量,就是一个名称,用来存储和代表不同值的东西 多种定义方式:var / let / const / function / import / class
//1. ES3
var a=12;
//2. ES6--let
let b=1;
b=2;
//3. ES6--const:CONST创建的变量,存储的值不能被修改(可以理解为叫做常量)--其实是指针不能别修改
const c=1; //=>undefined
c=2 //=>VM218:1 Uncaught TypeError: Assignment to constant variable.
//4. 创建函数也相当于创建变量
function fn(){}
//5. 创建类也相当于创建变量
class A{}
//6. ES6的模块导入也可以创建变量
import B from './B.js';
//7. Symbol 创建唯一值
let m=Symbol(100)
let n=Symbol(100)
m==n;//=> false
JS中的命名规范
- 严格区分大小写
- 不以数字开头的数字、字母、下划线_、$组成
- 使用驼峰命名法:首字母小写,其余每一个有意义单词的首字母都要大写(命名尽可能语义化明显,使用英文单词)
常用的缩写:
add/insert/create/new(新增)、update(修改)、delete/del/remove/rm(删除)、sel/select/query/get(查询)、info(信息)… 4. 不能使用关键字和保留字 当下有特殊含义的是关键字,未来可能会成为关键字的叫做保留字
代码强迫症(代码洁癖):良好的编程习惯、极客精神
JS中常用的数据类型
- 基本数据类型
- 数字number 常规数字和NaN
- 字符串string 所有用单引号、双引号、反引号(撇)包起来的都是字符串
- 布尔boolean true/false
- 空对象指针null
- 未定义undefined
- 引用数据类型
- 对象数据类型object
- {} 普通对象
- [] 数组对象
- /^[+-]?(\d|([1-9]\d+))(.\d+)?$/ 正则对象
- Math数学函数对象
- 日期对象
- ...
- 函数数据类型function
- 对象数据类型object
1. number数字类型
-- 包含:常规数字、NaN
- NaN:not a number:不是一个数,但它属于数字类型 typeof NaN=>"number"
- NaN和任何值都不相等:NaN != NaN ,所以不能用相等的方式判断是否为有效数字
- isNaN:检测一个值是否为非有效数字,是返回true,反之是有效数字返回false
- isNaN([val])--在使用isNaN进行检测的时候,首先会验证检测的值是否为数字类型,如果不是,先基于Number()这个方法,把值转换为数字类型,然后再检测
-- 把其它类型的值转换为数字类型
- Number([val]):浏览器内置方法,转换规则是按照浏览器底层的渲染规则
- 把字符串转换为数字,只要字符串中包含任意一个非有效数字字符(第一个除外)结果都是NaN,空字符串会变为数字0
- 布尔值转换为数字 true为1,false为0
- null undefined 转换为数字,null为0,undefined为NaN
- 把引用数据类转换为数字,是先把它基于toString方法转换为字符串,然后在转换数字
- parseInt([val],进制)/parseFloat([val],进制):转换规则是按照这两个方法本身的规则0 对于字符串来说,它是从左到右依次查找有效数字字符,直到遇到非有效数字字符,停止查找(不论后面是否还有数字都不找了),把找到的当作数字返回
== 进行比较的时候,可能要先把其它类型值转换为数字
== 比较的规律: 1.对象 == 字符串 --> 对象转换为字符串 2.null == undefined --> 这两者相等,但是和其它值都不相等 3.剩下两边不同的都是转换为数字
2. string字符串数据类型
- 所有用单引号、双引号、反引号(撇 ES6模板字符串)包起来的都是字符串
- 把其它类型值转换为字符串
- [val].toString()
- 字符串拼接
3. boolean 布尔数据类型
- 只有两个值 true/false
- 把其它类型值转换为布尔类型
只有0、NaN、空字符串''、null、undefined五个值转换为FALSE,其余都转换为TRUE(而且没有任何的特殊情况)
- Boolean([val])
- !/!!
- 条件判断
4. null / 5. undefined
null and undefined都代表没有;
- null:意料之中(一般都是开始不知道值,手动先设置为null,后期再给赋值操作)
let num=null; //=>let num=0;
一般最好用null作为初始的空值,因为零不是空值,它在栈内存中有自己的存储空间(占了位置)
···
num=12;
(种树时挖坑了但是没种树,这是0,null是连坑都没挖) 2. undefined:意料之外(不是我能决定的)
let num;//=>创建一个变量没有赋值,默认值是undefined
num=12;
6. object对象数据类之普通对象
{[key]:[value],···}任何一个对象都是由零到多组键值对(属性名:属性值)组成的。(并且属性名不能重复)
- 获取属性名对应的属性值
=>对象.属性名 =>对象[属性名] 属性名是数字或者字符串格式的 =>如果当前属性名不存在,默认的属性值是undefined
1.1 属性名只能是数字或者字符串,创建的时候键值对之间为冒号,每一对键值对之间有逗号隔开
console.log(person.name); //name是具体的属性名
console.log(person['name']); //name是具体的属性名
console.log(person[name]); //代表name是一个变量
console.log(person['age']);
console.log(person.sex);
1.2 当属性名是数字时双引号可以有也可以没有:obj['1'] & obj[1]是一样的
console.log(person[1]);
console.log(person['1']);
1.3 如果属性名是数字,则不能使用点的方式获取属性值,会报错
console.log(person.1);
//Uncaught SyntaxError: Unexpected number
1.4 对象的属性名虽然可以写成数字或者true等特殊格式,但是它们的数据类型都是字符串格式
let obj={
name:'cat',
age:2,
1:100,
2:200,
true:300
}
for(var key in obj){
if(key==='true') continue;//会跳过
// if(key===true) continue;//不会跳过
console.log(key+'---'+obj[key]);
}
- 设置属性名和属性值
属性名不能重复,如果属性名已经存在,不属于新增,属于修改属性值
person.GF='Lily';
person.name='Danny';
console.log(person.GF);
console.log(person.name);
- 删除属性
=>真删除:把属性彻底删除 =>假删除:属性还在,值为空
//假删除
person.weight=null;
console.log(person);//null
//真删除
delete person.weight;
console.log(person);//undefined
数组是特殊的对象 1.我们中括号中设置的是属性值,它的属性名是默认生成的数字,从零开始递增,而且这个数字代表每一项的位置,我们把其称为“索引” => 从零开始,连续递增,代表每一项的数字属性名 2.天生默认一个属性名length,存储数组的长度
let ary = [12,'aa',true,13];
console.log(ary);
console.log(ary.length);
console.log(ary['length']);
console.log(ary[1]);
//第一项索引0,最后一项索引ary.length-1;
console.log(ary[ary.length-1]);//获取最后一项
let ary=[1,2,3,4];
console.dir(ary);
/*
Array(4)
0: 1
1: 2
2: 3
3: 4
length: 4
__proto__: Array(0)
ary={
0:1,
1:2,
2:3,
3:4,
length:4
}
数字作为索引(KEY属性名)
length代表长度
ary[0] 根据索引获取指定项的长度
ary.length 获取数组的长度
ary.length-1 最后一项的索引
*/
对象的知识点扩充
扩展思考题: 已知下面代码可以修改BOX元素的样式
box.style.color = 'red';
那么下面的写法是否可以写成修改元素的样式,如果不可以是为什么?
//第一种方案
let AA = box.style;
AA.color = 'red';
//第二种方案
let BB = box.style.color;
BB = 'red';
哪种方式可以实现,不能实现是因为什么? 答:第一种可以实现,因为 element.style 是一个对象指针(引用数据类型的值),AA和box.style是同一个对象指针,所以当AA的值改变的时候,box.style的值也改变了。 第二种 element.style.color 是一个具体的值,是字符串类型(基本数据类型),如果此时box.style.color的值是'pink',那么这个等式“let BB = box.style.color;”相当于“let BB = "pink";”
box.style.color = "pink";
let BB = box.style.color;//=>let BB = "pink";
BB = 'red';
BB => 'red'
box.style.color //=> 'pink';
通过document.getElementById方法获取的元素是对象数据类型的值
let box = document.getElementById('box');
console.log(typeof box);//=>'object'
//基于.dir可以看到一个对象的详细信息
/*
id:操作元素的ID值
className:操作元素的CLASS样式类的值
innerHTML:操作的元素的内容(可以识别标签)
innerText:和innerHTML的区别是不能识别标签
tagName:获取元素的标签名(一般大写)
……
style:操作元素的行内样式 属性值是一个新的对象CSSStyleDeclaration
()
*/
console.dir(box)
7. 函数 function
函数就是一个方法或者一个功能体,函数就是把实现某个功能的代码放到一起进行分装,以后想操作实现这个功能,只需要把函数执行即可 => “封装”:减少页面中的冗余代码,提高代码重复使用率(低耦合高内聚)
- 创建函数
- 形参
- 返回值
- 执行值
- 实参
- arguments
- 函数底层运行机制
- ……
- 创建函数
function [函数名]([形参变量1],...){
//函数体:基于JS完成需要实现的功能
return [处理后的结果];
}
function fnName(){}
- 调用函数--函数名([实参]);
fnName();
创建函数分析:
function fn(){
var res = null;
res =n+m;
return res;
}
赋值操作: 第一步:创建右边的值; 第二步:创建左边的变量; 第三步:将值和变量关联在一起
- 创建函数,首先开辟一个堆内存,这个堆内存将函数体中的代码以字符串的形式存储
var AA = fn(10,20);
等号右边一定是值,所以先把fn函数执行,再把执行后的返回结果和AA关联起来 (函数的返回值只看return,有return后面返回值是什么就是什么,没有return就是undefined)
console.log(AA);
每一次函数执行的目的都是把函数体中的代码(先从字符串变为代码)执行=>形成一个全新的私有栈内存
fn(100,200)
函数每一次执行都会形成一个全新的私有栈内存(执行上下文),然后把函数代码从头到尾再执行一遍 VO 和 AO阶段:JS编译过程涉及的概念。
JS编译过程,VO,AO
首先关于整个生命周期,最重要的是要了解它的编译过程。 发现有代码调用了一个函数 在执行这个function之前,创建一个执行上下文(execution context),也可以叫执行环境。 进入创建阶段(VO创建) a. 初始化作用域链(scope chain) b. 创建变量函数(variable object / VO) c. 创建参数对象(arguments object,传进来的参数),检查上下文,初始化其名字和值,以及建立引用对象的拷贝。 d. 扫描上下文中的函数声明 e. 为每一个扫描到的函数声明在VO中创建一个属性,命名为函数的名字,指向了存储空间中的对应函数。 f. 如果函数名称已经存在了,这个引用指针将被重写为新的这一个。 g. 扫描上下文中的变量声明 h. 为每一个扫描到的变量声明在VO中创建一个属性,命名为变量的名字,初始化值为undefined。 i. 如果变量名在内存中已经存在了,就跳过。 j. 决定上下文中this的指向。 执行阶段(VO => AO) a. 执行/解释上下文中的function,为变量赋值 b. 代码按行执行 就个人理解,他们的相应概念和包含内容如下。
scope :变量/函数起作用的区域 scope chain : 保证对执行环境有权访问的所有变量和函数的有序访问。相当于VO + [scope] 我们可以将作用域定义为一套规则,用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找,作用域链是这套规则的具体实现。
execution context = {VO, this, [scope]}
this : 函数/方法的拥有者
JS中的数据类型检测
- typeof [val] :用来检测数据类型的运算符
- instanceOf : 用来检测当前实例是否隶属于某个类
- constructor : 基于构造函数检测数据类型(也是基于类的方式)
- Object.prototype.toString.call() : 检测数据类型最好的方法
第一个:基于typeof检测出来的结果:
- 首先是一个字符串
- 字符串中包含对应的类型
局限性:
- typeof null=>"object" 但是null并不是对象
- 基于typeof无法细分出当前值是普通对象还是数组对象等,因为只要是对象数据类型,返回的结果都是“object”
console.log(typeof 1);
console.log(1);//控制台输出是有颜色的,黑色是字符串,蓝色是数字
面试题--"string" 字符串
console.log(typeof typeof typeof []);
// typeof 检测出来的都是字符串,所以只要两个及以上同时检测,最后结果必然是“string”
第二个:js中的instanceof运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。 某个实例 instanceof(从属于) 某个类,是就返回true,不是返回false
function Person(){};
var p =new Person();
console.log(p instanceof Person);//true
第三个:某个实例.constructor === 某构造函数
function employee(name, job, born) {
this.name = name;
this.job = job;
this.born = born;
}
var bill = new employee("Bill Gates", "Engineer", 1985);
console.log(bill.constructor === employee);//true
console.log(bill.constructor);//[Function: employee]
/*
输出:
function employee(name, job, born)
{this.name = name; this.job = job; this.born = born;}
*/
第四个:检测数据类型 Object.prototype.toString.call();
//1. 检测直接数据类型
Object.prototype.toString.call();//=>"[object Undefined]"
Object.prototype.toString.call();//=>"[object Undefined]"
Object.prototype.toString.call([1,2]);//=>"[object Array]"
Object.prototype.toString.call(null);//=>"[object Null]"
Object.prototype.toString.call(undefined);//=>"[object Undefined]"
Object.prototype.toString.call(12);//=>"[object Number]"
Object.prototype.toString.call('aa');//=>"[object String]"
Object.prototype.toString.call(true);//=>"[object Boolean]"
Object.prototype.toString.call(NaN);//=>"[object Number]"
//2. 检测引用数据类型
Object.prototype.toString.call({});//=>"[object Object]"
Object.prototype.toString.call(/ /);//=>"[object RegExp]"
Object.prototype.toString.call(Math);//=>"[object Math]"
Object.prototype.toString.call(Date);//=>"[object Function]"
function fn(){}
Object.prototype.toString.call(fn);//=>"[object Function]"
JS中的操作语句:判断、循环
判断--条件成立做什么?不成立做什么?
- if/else if/else
- 三元运算符
- switch case
- if/else
if(条件){
条件成立执行
}else if(条件2){
条件2成立执行
}
……
else{
以上条件都不成立执行
}
- == VS ===
==: 相等 (如果左右两边数据值类型不同,是默认先转换为相同的类型,然后比较) '5' == 5; =>TRUE ===:绝对相等(如果类型不一样,肯定不相等,不会默认转换数据类型) '5' === 5 ;=> FASLSE
项目中为了保证业务的严禁,推荐使用 ===
循环--重复做某些事情就是循环
- for循环
- for in循环
- for of循环
- while 循环
- do while循环
js控制语句
- if()else{}
- switch(){}
- for(){}
- for in
- while
- do()while
- label
- break/continue
- with()
- js中的函数没有重载
函数的参数是一个内部的对象arguments 返回值
for in循环和for of循环
- for in循环:用来循环遍历对象中的键值对的(continue和break同样适用)
循环迭代对象的属性名 语法: for(var 变量(key) in 对象){} 对象中有多少组键值对,就循环执行几次(除非break结束)
var obj = {
name: 'dog',
age: 3,
friends: 'Lily,Danny',
1: 100,
2: 120,
3: 150
}
//每一次循环key变量存储的:当前对象的属性名
//获取属性名:obj[属性名] = obj[key]
//obj.key / obj['key']这样写不行,因为obj对象中没有属性名为key的
for (var key in obj) {
console.log('属性名:' + key + '--' + '属性值:' + obj[key])
}
//for in在遍历的时候优先循环数字属性名(从小到大)
/*
输出如下:
属性名:1--属性值:100
属性名:2--属性值:120
属性名:3--属性值:150
属性名:name--属性值:dog
属性名:age--属性值:3
属性名:friends--属性值:Lily,Danny
*/
- for...of:循环迭代对象的属性值
const array1 = ['a', 'b', 'c'];
for (const element of array1) {
console.log(element);
}
/*
'a'
'b'
'c'
*/