作用域
存储变量当中的值,使得这个值能够被访问和修改。使得js程序有状态的概念。
编译过程
- 引擎
从头到尾负责整个JavaScript程序的编译及执行过程。 - 编译器
负责语法分析及代码生成 - 作用域
负责搜集并维护有所有声明的标识符组成的一系列查询,并实施一套非常严格的规则,确定挡前执行的代码对这些标识符的访问权限。
作用域是一套规则,用于确定在何处以及如何查找变量。
作用域嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。
异常
ReferenceError同作用域判别失败相关,而TypeError则代表作用域判别成功了,但是对结果的操作是非法或不合理。
作用域
作用域分为
- 词法作用域
- 动态作用域
词法作用域
动态作用域
词法阶段
提升
任何声明在某个作用域内的变量,都将附属与这个作用域。
变量声明和函数声明都会被提升。
foo() // TypeError
bar() // ReferennceError
var foo = funtion bar() {
// ...
};
可以理解为
var foo;
foo(); // TypeError
bar(); // ReferenceError
foo = function() {
var bar = ...self...
}
作用域闭包
类
类的设计模式:实例化、继承、多态
面向对象强调的是数据和操作数据的行为本质上是关联的。
类是一种设计模式。
类型
Javascript有7个内置类型
- null
- undefined
- string
- number
- boolean
- object
- symbol (ES6新增)
typeof
对变量进行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型。总是返回字符串。
typeof undefined === "undefined"; // true
typeof true === "boolean"; // true
typeof 42 === "number";
typeof "42" === "string";
typeof { life: 42 } === "object"
typeof Symbol() === "symbol"
// nul有问题
typeof null === "object"
// 复合条件检测
var a = null;
(!a && typeof a === "object"); // true
funtion也是JavaScript的一个内置函数。实际上是object的“子类型”,他有一个内部属性[[Call]], 该属性使其可以被调用。
函数不仅是对象,还可以拥有属性。函数对象的length属性是其声明的参数的个数
值和类型
变量是没有类型,只有值才有,变量随时可以持有任何类型的值。
值
数组
在Javascript中,数组可以容纳任何类型的值,可以是字符串、数字、对象,甚至是其他数组。
类数组
一些DOM查询操作会返回DOM元素列表,并非真正意义上的数组,但十分相似,还有通过arguments对象将函数参数当做列表来访问(ES6废除)。
工具函数slice(..)经常被用于这类转换:
function() {
var arr = Array.prototype.slice.call(arguments);
arr.push("bam");
console.log(arr);
}
foo("bar", "baz"); // ["bar", "baz", "bam"]
Array.from 也能实现同样的功能
...
var arr = Array.from(arguments);
···
字符串
字符串经常被当做字符串数组。
var a = "sfsdfa"
// 并非总是合法语法
a[1]
// 正确
a.charAt(1)
call、apply和bind
实现自己的bind
Function.prototype.myBind = function() {
if (typeof this !== "function") {
throw new Error('Must call with a function');
}
const _func = this;
const realThis = args[0] || window;
return function(...args) {
return _func.call(realThis, ...args)
}
}
补充get和post请求在缓存方面的区别
post/get的请求区别,具体不再赘述。
补充补充一个get和post在缓存方面的区别:
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。
类的创建
- 实例方法
- 原型方法
类的继承---原型链继承
构造继承 使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
实例继承和拷贝继承
组合继承:相当于构造继承和原型链继承的组合体。通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用
寄生组合继承:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造的时候,就不会初始化两次实例方法/属性
异步回调地狱解决
promise、generator、async/await
事件流
HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。
什么是事件流:事件流描述的是从页面中接收事件的顺序,DOM2级事件流包括下面几个阶段。
-
事件捕获阶段
-
处于目标阶段
-
事件冒泡阶段
mouseover和mouseenter的区别
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
js的new操作符做了哪些事情
new 操作符新建了一个空对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象。
改变函数内部this指针的指向函数(bind,apply,call的区别)
通过apply和call改变函数的this指向,他们两个函数的第一个参数都是一样的表示要改变指向的那个对象,第二个参数,apply是数组,而call则是arg1,arg2...这种形式。通过bind改变this作用域会返回一个新的函数,这个函数不会马上执行。
js拖拽功能的实现
异步加载js的方法
defer:只支持IE如果您的脚本不会改变文档的内容,可将 defer 属性加入到<script>标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。
async,HTML5属性仅适用于外部脚本,并且如果在IE中,同时存在defer和async,那么defer的优先级比较高,脚本将在页面完成时执行。
创建script标签,插入到DOM中
简单实现Node的Events模块
数组对象去重
let res = []
let obj = {}
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i].name]) { // name 对应数组中的name
res.push(arr[i])
obj[arr[i].name] = true
}
}
console.log(res)
let obj = {}
let newArr = []
newArr = arr.reduce((item, next) => {
obj[next.name] ? ' ' : obj[next.name] = true && item.push(next)
return item
}, [])
性能优化
减少HTTP请求
使用内容发布网络(CDN)
添加本地缓存
压缩资源文件
将CSS样式表放在顶部,把javascript放在底部(浏览器的运行机制决定)
避免使用CSS表达式
减少DNS查询
使用外部javascript和CSS
避免重定向
图片lazyLoad
能来讲讲JS的语言特性吗
运行在客户端浏览器上;
不用预编译,直接解析执行代码;
是弱类型语言,较为灵活;
与操作系统无关,跨平台的语言;
脚本语言、解释性语言
如何判断一个数组(讲到typeof差点掉坑里)
Object.prototype.call.toString()
instanceof
import 和 require的区别
-
第一点
require是commonjs规范
import是es6规范 -
第二点
require是运行时调用
import是编译时调用,必须放在文件开头
image.png -
导入一个模块时,require获取的是带出值的拷贝;而ES6 Module则是值的动态映射,并且这个映射是只读。
跨域技术
- JSONP
- CORS (跨域资源共享)
- 图像PING
- WebSocket
javascript创建web socket后,会有一个http请求发送到服务器以建立连接。在取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换到websocket协议。 - 反向代理
- nginx 反向代理
- http-proxy-middleware
- postmassage
短轮询
浏览器定时向服务器发送请求。
长轮询
页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到有数据可发送。发送完数据后,浏览器关闭连接,随机又发送一个到服务器的新请求。
闭包
形式上: 函数内部嵌套函数。
闭包就是能够读取其他函数内部变量的函数。
输入url的过程
1.根据域名到DNS中找到IP
2.根据IP建立TCP连接(三次握手)
3.连接建立成功发起http请求
4.服务器响应http请求
5.浏览器解析HTML代码并请求html中的静态资源(js,css)
6.关闭TCP连接(四次挥手)
7.浏览器渲染页面
浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象
深拷贝
深拷贝就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"。(深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象)
深拷贝函数:
ES6转ES5
class Person {
constructor (name) {
this.name = name;
}
greet () {
console.log(`Hi, my name is ${this.name}`);
}
greetDelay (time) {
setTimeout(() => {
console.log(`Hi, my name is ${this.name}`);
}, time);
}
}
ES5
'use strict'
var Person = (function() {
function Person(name) {
this.name = name;
}
var _proto = Person.prototype;
_proto.greet = function greet() {
console.log("Hi, my name is " + this.name);
};
_proto.greetDelay = function greetDelay(time) {
var _this = this;
setTimeout(function () {
console.log("Hi, my name is " + _this.name);
}, time);
};
return Person;
})()
垃圾收集机制
javascript具有自动垃圾收集机制,主要分为2类
-
标记清除(常用)
-
引用计数
网友评论