数组
- 作用于原数组
- push(end)
- unshift(start)
- pop(end)
- shift(start(
- splice(any position)
- reverse
- sort
- fill
- 创建新的数组
- map
- filter
- slice
- concat
- flat
- flatMap
- 索引
- indexOf
- findIndex
- find
- 返回布尔值
- include
- some
- every
- join
- 循环
- forEach
- reduce
网站
发生了什么
当你在浏览器里输入一个网址时(在我们的例子里就是走向商店的路上时):
- 浏览器在域名系统(DNS)服务器上找出存放网页的服务器的实际地址(找出商店的位置)。
- 浏览器发送 HTTP 请求信息到服务器来请拷贝一份网页到客户端(你走到商店并下订单)。这条消息,包括其他所有在客户端和服务器之间传递的数据都是通过互联网使用 TCP/IP 协议传输的。
- 服务器同意客户端的请求后,会返回一个“200 OK”信息,意味着“你可以查看这个网页,给你~”,然后开始将网页的文件以数据包的形式传输到浏览器(商店给你商品,你将商品带回家)。
- 浏览器将数据包聚集成完整的网页然后将网页呈现给你(商品到了你的门口 —— 新东西,好棒!)。
解析组成文件的顺序
当浏览器向服务器发送请求获取 HTML 文件时,HTML 文件通常包含 "link" 和 "script" 元素,这些元素分别指向了外部的 CSS 样式表文件和 JavaScript 脚本文件。了解这些文件被浏览器解析的顺序是很重要的:
- 浏览器首先解析 HTML 文件,并从中识别出所有的 "link" 和 "script" 元素,获取它们指向的外部文件的链接。
- 继续解析 HTML 文件的同时,浏览器根据外部文件的链接向服务器发送请求,获取并解析 CSS 文件和 JavaScript 脚本文件。
- 接着浏览器会给解析后的 HTML 文件生成一个 DOM 树(在内存中),会给解析后的 CSS 文件生成一个 CSSOM 树(在内存中),并且会编译和执行解析后的 JavaScript 脚本文件。
- 伴随着构建 DOM 树、应用 CSSOM 树的样式、以及执行 JavaScript 脚本文件,浏览器会在屏幕上绘制出网页的界面;用户看到网页界面也就可以跟网页进行交互了。
解析方式
原始类型与引用类型
1、值类型 (基本数据类型)
字符串(String)、数字 (Number)、布尔 (Boolean)、对空(Null)、未定义(Undefined)、Symbol。
2、引用数据类型
对象 (Object)、数组 (Array)、函数 (Function)。
3、判断一个值是什么类型有什么方法?
原始类型存储在栈空间中
引用类型的存储同时占用栈空间和堆空间,栈中存储引用,堆中存储值
当引用类型的对象被赋值时,实际上是创建了两个引用,两个引用都指向堆中的真实数据,一个改变会导致另外一个同样改变
C语言中,引用类型在赋值时创建了两个指针,指针的值是同一个地址(对象在堆中数据的地址)
对象拷贝
浅拷贝
适用于对象内存放原始数据类型的情形,不适用于多重嵌套对象的情形
let message = {name:"xiaoming",age:19}
function copyObj(obj){
for (let i in obj){
tObj = {};
let tObj[i] = obj[i];
}
return tObj;
}
let newObj = copyObj(message)
深拷贝
利用递归逐步拷贝对象
let message = {name:"xiaoming",age:19}
function copyObj(obj){
for (let i in obj){
tObj = {};
if(obj[i] instanceof Object){
let tObj[i] = copyObj(obj[i])//递归
}else{
let tObj[i] = obj[i];
}
let tObj[i] = obj[i];
}
return tObj;
}
let newObj = copyObj(message)
JSON字符串
let message = {name:"xiaoming",age:19}
function copyObj(obj){
newStr = JSON.stringify(obj)
newObj = JSON.parse(newStr)
return newObj;
}
let newObj = copyObj(message)
包装对象
问题:为什么原始类型可以调用方法和属性?
字符串类型(原始数据类型)对应的split方法的本质:首先创建一个String对象,进而调用对象的split方法,返回一个数组类型的对象,并且销毁新创建的String对象
string str1 = "hello"
let s = str1.split(" ")
//str.split(" ") <==> new String(str).split(" ")
console.log(s)
创建包装对象的构造函数可以实现类型转换
实际开发中:尽量不主动创建包装对象
类型转换
显示转换(强制转换)
利用包装对象的构造函数可以实现强制类型转换
<input type="text" id="num1">
<input type="text" id="num2">
<button>+</button>
显示转换包括:
- Num()
- String()
- Boolean()
- toString()|对null和undefined类型不适用
- parseInt()转换为数值类型
- parseFloat()转换为数值类型
5,6 对于字符串转数值类型十分常用
隐式转换(自动转换)
布尔值的转换
if(){},while(){}进行判断时会自动将括号内的数据类型转换为boolean类型
studentList = null;
//从数据库中获取studentList
if (studentList){//若获取成功
//则执行下一步操作
}
与&&
&&的转换:将左侧的值转换为布尔值进行判断,
如果为true,则返回右侧的值;false,返回左侧的值
或||
&&的转换:将左侧的值转换为布尔值进行判断,
如果为false,则返回右侧的值;true,返回左侧的值
非!
布尔值取反
==
仅仅比较值,若二者的类型不相同,会被自动转换为相同的数据类型进行值的比较
===
先判断数据类型,若数据类型都不相同,则直接返回false
实际开发:用===,提升性能,避免歧义
作用域
ES6之前,使用var定义变量,没有块级作用域的概念
ES6之后,使用let/const定义变量,有块级作用域的概念 : 使用let定义的变量只在当前语句块中生效
进而解释了
if(true){
var a = 2;
}
console.log("ES5:"a)
if(true){
let a = 2;
}
console.log("ES6:"a)
统一使用let定义变量
引入外部模块,定义函数表达式时可以使用const
- let定义的变量是可以被重复赋值的,const则不可以
- const在定义变量的同时必须赋值,而let定义变量时可以理解为先定义再赋值
- 相同点:let不能定义同名变量,const不能定义同名字常量
const定义对象时,由于引用地址(指针)的特性,保证的是指向的内存地址不变,因此使用const定义的对象是可以改变属性的
作用域链
底层的作用域可以访问上层及更上层的作用域,而上层的作用域不能访问底层的变量
闭包和模块化
函数嵌套,内部函数就叫做闭包
内部函数没有执行完,外部函数内存空间不会被销毁,进而可以重复使用内部函数访问外部函数中的变量
可以用来实现模块化或封装代码
由于块级作用域的概念,导致在全局环境下无法访问内部函数,因此使用闭包:
function outFun(){
let n = 10;
function innerFun(){
console.log(n);
}
return innerFun;//注意:此处返回的是函数名,而不是函数的返回值innerFun()
}
let fn = outFun();//此时的fn就是innerFun,实现了在全局环境下访问内部函数的效果
fun();
fun();//可重复调用
内部函数没有执行完毕,所以let fun = outFun()语句执行后outFun中的变量不会被立即销毁,从而能够调用fun()函数即内部函数,当内部函数执行完毕后,outFun()在栈中空间自动回收
个人理解:可以把函数理解为存储在堆中,let fun = outFun()只是创建了2个指针,指向函数在堆内存中的地址而已,fun()执行就相当于执行了outFun的内部函数,再执行outFun中的其他部分,进而回收栈内存
历史遗留问题:闭包解决不同模块之间互不影响
let a = (function(){
let n = 10;
let m = 20;
function innerFun(){
console.log(n+m);
}
return innerFun;//注意:此处返回的是函数名,而不是函数的返回值innerFun()
})()
let b = (function(){
let n = 10;
let m = 20;
function innerFun(){
console.log(n-m);
}
return innerFun;//注意:此处返回的是函数名,而不是函数的返回值innerFun()
})()
a()
b()
开发中实现模块化:ES6的语法,node的语法
原型对象
prototype用来拓展内置对象的功能
<script>
function User(){
this.login() = function(){
console.log("Successfully Login.")
}
}
Admin.prototype = new User()
function Admin(){}
var admin = new Admin()
admin.login()
</script>
原型链
Array.prototype.toString = function(){
return "重写toString()方法"
}
Date.prototype.dateFormate = function(){
return
}
不要轻易重写prototype的方法
拓展内置对象
原型链中的方法和属性没有被复制到其他对象——它们被访问需要通过前面所说的“原型链”的方式。
prototype属性大概是 JavaScript 中最容易混淆的名称之一。你可能会认为,this关键字指向当前对象的原型对象,其实不是(还记得么?原型对象是一个内部对象,应当使用__proto__访问)。prototype属性包含(指向)一个对象,你在这个对象中定义需要被继承的成员。
对象的__proto__属性返回其原型对象,每个对象都有其对应的原型对象
一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 prototype 属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。例如:
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }
// 等等……
this
指向调用该方法的对象
谁调用方法,this指向谁
构造函数中的this,指向new创建出来的对象
new创建出来的对象是谁,this就指向谁
触发事件
Dom事件被触发时,事件监听函数指向触发事件的DOM对象
当点击button时,this指向button对应的Dom对象