1: js的基本数据类型以及数据类型的转换,es6新增的数据类型
- 原始类型--栈stack
- 引用类型--堆heap(栈中存的是引用类型的地址)
- 原始值: Number String Boolean undefined null
- 引用值:array object function date
2:对象的原型 原型链
- (1) __ proto __
查看对象的原型
- (2)prototype
function Person(name,age){
this.name = name;
this.age = age
}
var person = new Person('tom', 12)
console.log('person', person)
- (3)constructor 查看对象的构造函数
- (4) instanceof
A instanceof B.
判断A对象是不是B构造函数构造出来的
看A对象的原型链上有没有B的原型
- (5)构造函数构造对象的三步
// 创建一个新对象
//将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
//执行构造函数中的代码
//返回新对象
function Test(){
console.log(111)
}
var test1 = new Test()
- (6)hasOwnProperty
//判断这个属性是不是该对象原型上的属性
//用来判断这个属性是不是继承来的
3: this
1:首先this是个对象,至于它具体代表谁,请看大屏幕自己体会
2: 函数预编译过程 this —> window
3: 全局作用域里 this—window
4:call/apply 可以改变函数运行时 this的指向
5:obj.func func()里边的this指向obj
//请看大屏幕
var name = 'haha'
var obj ={
name: 'hehe',
foo: function(){
console.log(this.name)
}
}
var foo = obj.foo
foo(); // 'haha'
obj.foo() // 'hehe'
var x = 1, y = z = 0;
function add(n) {
return n = n + 1;
}
y = add(x)
function add(n) {
return n = n + 3;
}
z = add(x)
var name = '222';
var a = {
name: '111',
say: function(){
console.log(this.name)
}
}
var fun = a.say;
fun(); // 222
a.say() // 111
var b = {
name: '333',
say: function(func){
func()
}
}
b.say(a.say) // 222
b.say() = a.say();
b.say(); //333
4:继承的几种写法
经历了四个阶段
*传统形式--原型链继承方式
*借用构造函数(call/apply)方式
*共享原型
*中间层圣杯模式
//借用原型实现属性的继承-缺点是继承了过多没用的属性
Person.prototype.firstName = 'liu';
function Person(name,age){
this.name = name;
this.age = age
}
var person = new Person('hehe',18) //
Grand.prototype.lastName = 'Ji'
function Grand(){
}
var grand = new Grand();
Father.prototype = grand;
function Father(){}
var father = new Father()
Son.prototype = father;
function Son(){}
var son = new Son();
// 用call/apply实现--缺点每次都要多走一个构造函数
function Person(name,age){
this.name = name;
this.age = age
}
function myPerson(name,age,sex) {
Person.call(this,name,age)
this.sex = sex;
}
var person1 = new myPerson('haha', 20, 'girl')
// 共享原型方式-缺点:不能随便改动自己的原型
Father.prototype.lastName = 'Deng';
function Father(){}
function Son(){}
Son.prototype = Father.prototype;
//如果Father的原型更改,Son的也会跟着更改
// 实现继承
function inherit(Target, Origin){
Target.prototype = Origin.prototype
}
// 中间层模式--类似与对象拷贝 call apply改变this的指向
function inherit(Target,Origin){
function F(){
}
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target
}
5:call 和apply方法
// 改变this的指向
Function.prototype.mycall = function (context) {
console.log('context', context)
if (typeof context == 'undefined' || typeof context == 'null') {
context = window
}
// context = context || window;
context.fn = this
const args = [...arguments].slice(1)
const result = context.fn(...args)
delete context.fn
return result
}
Function.prototype.myApply = function (context,arr){
context = context || window;
context.fn = this
const result = context.fn(...arr)
delete context.fn
return result
}
6:判断对象是数组还是对象
-
instanceof
-
constructor
-
toString + call/apply
var test = {}
test instanceof Array //
test instanceof Object//
test.constructor == Array
test.constructor == Object
Object.prototype.toString.call(test) //"[object Array]"
Object.prototype.toString.call(test) //"[object Object]"
7:预编译
- 预编译发生在函数发生的前一刻
- 函数声明整体提升
- 变量 --声明提升
- 预编译发生的过程
1: 创建AO对象(执行期上下文)-Activation Object
2:找形参和变量声明,将变量和形参作为AO属性,值为undefined
3:将实参值和形参统一
4:在函数体里找到函数声明,值赋予函数体
-------
全局预编译的过程
1: 创建GO对象(执行期上下文) Global Object
2:变量声明,将变量作为GO属性名,值为undefiend
3: 在函数体里面找到函数声明,值赋予函数体
----
eg1:
function fn(a){
console.log(a);
var a = 123;
console.log(a)
function a(){};
console.log(a)
var b = function(){};
console.log(b)
function d(){}
}
fn(1) // 判断函数的执行顺序
// function 123 123 function
eg2:
function test(a,b) {
console.log(a);
c = 0;
var c;
a = 3;
b = 2;
console.log(b);
function b(){};
function d(){}
console.log(b)
}
test(1) // 执行结果
// 1 2 2
eg3:
console.log(test)
function test(test){
console.log(test)
var test = 234
console.log(test)
function test(){}
}
eg4:
global = 100;
function fn(){
console.log(global)
global = 200;
console.log(global)
var global = 300;
}
fn()
var global;
eg5:
function test(){
console.log(b)
if(a){
var b = 100;
}
c = 234
console.log(c)
}
var a;
test();
a = 10;
console.log(c)
8:作用域 && 作用域链
-
作用域[[scope]]: 每个js函数都是一个对象,对象中有的属性我们可以访问,但有些不可以,这些属性仅供js引擎存取,[[scope]]就是其中一个,它值得就是我们所说的作用域,其中存储了运行期上下文的集合
-
作用域链:作用域中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链
-
执行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象AO,一个执行期上下文定义了一个函数的执行环境,函数每次执行时对应的执行上下文是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文也会被销毁
9:闭包
从作用域的角度来讲,内部函数会继承外部函数的作用域,当外部函数释放之后,由于内部函数被保存到了外部,就导致了原来应该释放的作用域链未被释放,形成了闭包
当内部函数被保存到外部时,将会生成闭包,闭包会导致原有的作用域链不释放,造成内存泄漏
单反内部函数被保存在外部了,一定生成闭包
闭包的几点用处
*实现共有变量--函数累加器
*用作缓存
*可以实现封装,属性私有化
*模块化开发,防止污染全局变量
//实现累加器
functon addNum(){
var num = 0;
return function test(){
num +=1;
console.log(num)
}
}
var numShow = addNum()
numShow(); // 1
numShow(); // 2
//
function test(){
var arr = []
for(var i=0;i< 10;i++){
arr[i] = function(){
console.log(i)
}
}
return arr
}
test()[0]() // 10
10: js的事件循环机制
11:防抖和节流
防抖debounce
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
// 滚动条加载事件
//模糊匹配
//防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null) {
clearTimeout(timeout);
}
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
// 如何理解
当第一次点击的时候 timeout = null,正常执行,这个时候function被return到全局,timeout不被释放,导致在全局中存在,如果再次操作,就会被清空,此时timeout = null ,然后重新被执行,如果你一直操作,那我就按照最后一次的时间在往后延迟固定间隔
函数节流: 当持续触发事件时,保证一定时间段内只调用一次事件处理函数,
// 举例->持续触发scroll事件时,并不立即执行handle函数,而是
//每隔1000毫秒才会执行一次handle函数
// 节流
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000))
// 如何理解
当第一次点击的时候,prev是当前时间,prev被保存到全局中去,当再一次触发事件操作的时候,判断当前时间跟上一次发生的时间差,如果大于的话,执行,此时
prev保存的就是上一次执行完的时间
依然是闭包,
12:输入url回车后的过程 || http和https区别
1:读取缓存
2:应用层DNS域名解析解析
3:TCP三次握手链接(TCP 三次握手用以同步客户端和服务端的序列号和确认号,并交换 TCP 窗口大小信息。)(为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误)
4:应用层客户端发送http请求(http请求包括报头和请求主体)
5:服务器处理请求并返回http报文(响应行(request line)、响应头部(header)、响应主体)
6:浏览器解析渲染页面
7:断开链接TCP四次握手
互联网各设备间通讯都遵循TCP/IP协议,利用TCP/IP协议族进行网络通信时,会通过分层顺序与对方进行通信

13:http协议相关
14:跨域问题
- 1:jsonp
通源策略(域名 || 端口 || 协议)
- 2:CORS
服务端设置
15:关于浏览器缓存
1: 强缓存 —通过http请求头中的Cache-Control和Expire两个字段控制
Expire--HTTP1.0 标准的字段
Cache-Control — ‘public, max-age=xxx’
表示在xxx秒内再次访问该资源,均使用本地的缓存,不再向服务器发起请求
2:协商缓存—
协商缓存最大的问题就是每次都要向服务器验证一下缓存的有效性
更多的命中强缓存
在更新版本的时候,顺便把静态资源的路径改了,这样,就相当于第一次访问这些资源,就不会存在缓存的问题了。
综上所述,我们可以得出一个较为合理的缓存方案:
* HTML:使用协商缓存。
* CSS&JS&图片:使用强缓存,文件命名带上hash值。
hash:跟整个项目的构建相关,构建生成的文件hash值都是一样的,只要项目里有文件更改,整个项目构建的hash值都会更改。
chunkhash:根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的hash值。
contenthash:由文件内容产生的hash值,内容不同产生的contenthash值也不一样。
1:判断是否有强制缓存
第一次返回http响应的时候,如果有cache-control
则说明设置了强制缓存 — 就是第二次请求这个资源时,在浏览器端,先去判断cache-control的值, 如果成立,直接在缓存中获取,不去发送http请求了 200ok(from memory cache)
2: 判断是否有协商缓存
第一次返回http响应的时候,如果响应中有Etag 或者last-modified
字段,则说明服务器也希望这个资源被缓存
协商缓存需要发送http请求,
Etag ——if-none-match
last - modified —— if-modified-since
有一个校验的过程
如果校验通过,则寿命第二次请求的数据并没有发生变化,服务器会返回304状态码