JavaScript Basic Memo
1、this 的指向
1)、由 new 调用?绑定到新创建的对象。
2)、 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
3)、 由上下文对象调用?绑定到那个上下文对象。
4)、 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。
2、new关键字的过程
- 创建一个新对象
- 将 this 绑定到新创建的对象上
- 在新创建的对象上添加一个叫 __proto__ 的属性,指向构造函数的原型 prototype 对象
- 返回新创建的对象 return this
function Student(name, age) {
this.name = name;
this.age = age;
}
var first = new Student('John', 26);
创建了一个新对象 —— first 对象
this 绑定到 first 对象。所有 this 引用指向 first。
添加__proto__。first.__proto__ 现在指向 Studnet.prototype。
所有事情完成后,我们将新的 first 对象返回出来赋值给新的 first 变量。

3、call、apply 和 bind 的区别
共同点:都是改变代码运行时的上下文
不同点:apply 接收一个包含多个参数的数组,而 call 与 bind 接收的是若干个参数的列表,另外 bind 会创建一个被初始化参数改造的新函数
// 一个简单的实现
Function.prototype.bind = function(ctx) {
var fn = this; return function() {
fn.apply(ctx, arguments);
} // ......
}
4、获取对象属性的方法
Object.getOwnPropertyNames vs Object.keys
var a = {};
Object.defineProperties(a, {
one: {enumerable: true, value: 'one'},
two: {enumerable: false, value: 'two'},
});
// 只能获取 enumerable 为 true 即可枚举的属性
// ["one"]
Object.keys(a);
// 可不可枚举都能得到
// ["one", "two"]
Object.getOwnPropertyNames(a);
5、闭包
定义:是一个由函数和其词法作用域绑定在一起所形成的组合结构
特征:函数可以记住并访问其词法作用域,即时函数在当前的词法作用域外执行也可以
栗子:
function Eat() {
var desc = ' is eating';
function eat(animal) {
console.log(animal.name + desc);
}
return eat;
}
var eat = Eat();
//全局变量
var desc = '正在吃...';
var dog = {name: 'dog'};
// 闭包里的词法作用域(又叫静态作用域)是在函数创建时就和函数绑定了
// 知道输出结果了吧!
eat(dog);
// 还不明白,看这个
var x = 1;
function foo() {
console.log(x);
}
function bar(fn) {
var x = 2;
fn();
}
//输出?
bar(foo);
应用:模块加载器
var MyModules = (function Manager(){
var modules = {};
function define(name, deps, impl) {
for(var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
6、怎么理解JS模块化?有没有使用过webpack?
优点:提高代码的可维护性、可重用性,避免污染命名空间
7、怎么弥补 CSS in JS 不支持嵌套、keyframes、媒体查询等特性的缺陷 ?
使用 styled-components,它的实现原理是使用 ES6的模板字符串特性,解决了在 JSX 中编写 CSS 的各种问题,由于它最终是在 head 里插入 style,所以它支持所有 CSS 特性,同时又能获取 JS 强大的控制力,具体详情戳这里styled-components 背后的魔法
8、requestAnimationFrame 优化原理
requestAnimationFrame 会自动匹配 W3C 所建议的刷新频率,既不会因为频率太高,增加开销,也不会频率太低,造成动画丢帧卡顿。
优点:
- requestAnimationFrame 会把每一帧中所有的 DOM 操作使用 DocumentFragment 集中起来,在一次重绘或回流中完成
- 在隐藏或不可见的元素中,
requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量 - 如果页面不是激活状态下的话,会暂停调用来提升性能和电池寿命。
9、怎么突破 localStorage 的容量限制
- localstorage的跨域存储方案,使用postMessage
- IndexedDB 用于客户端存储大量结构化数据(包括, 文件/ blobs)。该API使用索引来实现对该数据的高性能搜索。
10、Event Loop 原理
当 js 代码执行时会将不同的变量存到堆(heap)和栈(stack)中。其中,堆中存放着一些对象,而栈中则存放着一些基础类型变量以及对象的指针。
而执行栈是指当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。
当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境,这个过程反复进行,直到执行栈中的代码全部执行完毕。

当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。
这个过程需记住当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
以下事件属于宏任务:
setInterval()setTimeout()
以下事件属于微任务
new Promise()new MutaionObserver()
那么,下面这段代码的结果是 ?
setTimeout(function () {
console.log(1);
});
new Promise(function(resolve,reject){
console.log(2)
resolve(3)
}).then(function(val){
console.log(val);
})
// 2 3 1
详细规则为:
当一个程序有:setTimeout, setInterval ,setImmediate, I/O, UI渲染,Promise ,process.nextTick, Object.observe, MutationObserver的时候:
1.先执行 macrotasks:I/O -》 UI渲染-》requestAnimationFrame
2.再执行 microtasks :process.nextTick -》 Promise -》MutationObserver ->Object.observe
3.再把setTimeout setInterval setImmediate【三个货不讨喜】 塞入一个新的macrotasks,依次:setTimeout ,setInterval --》setImmediate
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
// 结果是:3 4 6 8 7 5 2 1
11、Vue.js 中 nextTick 的实现原理
当观察到数据变化时,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会一次推入到队列中。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际(已去重的)工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MutationObserver,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。
Vue源码详解之nextTick:MutationObserver只是浮云,microtask才是核心! · Issue #6 · Ma63d/vue-analysis
12、Vue 实现原理

vue将data初始化为一个Observer并对对象中的每个值,重写了其中的get、set,data中的每个key,都有一个独立的依赖收集器。- 在
get中,向依赖收集器添加了监听 - 在mount时,实例了一个
Watcher,将收集器的目标指向了当前Watcher - 在
data值发生变更时,触发set,触发了依赖收集器中的所有监听的更新,来触发Watcher.update
const Observer = function(data) {
// 循环修改为每个属性添加get set
for (let key in data) {
defineReactive(data, key);
}
}
const defineReactive = function(obj, key) {
// 局部变量dep,用于get set内部调用
const dep = new Dep();
// 获取当前值
let val = obj[key];
Object.defineProperty(obj, key, {
// 设置当前描述属性为可被循环
enumerable: true,
// 设置当前描述属性可被修改
configurable: true,
get() {
console.log('in get');
// 调用依赖收集器中的addSub,用于收集当前属性与Watcher中的依赖关系
dep.depend();
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// 当值发生变更时,通知依赖收集器,更新每个需要更新的Watcher,
// 这里每个需要更新通过什么断定?dep.subs
dep.notify();
}
});
}
const observe = function(data) {
return new Observer(data);
}
const Vue = function(options) {
const self = this;
// 将data赋值给this._data,源码这部分用的Proxy所以我们用最简单的方式临时实现
if (options && typeof options.data === 'function') {
this._data = options.data.apply(this);
}
// 挂载函数
this.mount = function() {
new Watcher(self, self.render);
}
// 渲染函数
this.render = function() {
with(self) {
_data.text;
}
}
// 监听this._data
observe(this._data);
}
const Watcher = function(vm, fn) {
const self = this;
this.vm = vm;
// 将当前Dep.target指向自己
Dep.target = this;
// 向Dep方法添加当前Wathcer
this.addDep = function(dep) {
dep.addSub(self);
}
// 更新方法,用于触发vm._render
this.update = function() {
console.log('in watcher update');
fn();
}
// 这里会首次调用vm._render,从而触发text的get
// 从而将当前的Wathcer与Dep关联起来
this.value = fn();
// 这里清空了Dep.target,为了防止notify触发时,不停的绑定Watcher与Dep,
// 造成代码死循环
Dep.target = null;
}
const Dep = function() {
const self = this;
// 收集目标
this.target = null;
// 存储收集器中需要通知的Watcher
this.subs = [];
// 当有目标时,绑定Dep与Wathcer的关系
this.depend = function() {
if (Dep.target) {
// 这里其实可以直接写self.addSub(Dep.target),
// 没有这么写因为想还原源码的过程。
Dep.target.addDep(self);
}
}
// 为当前收集器添加Watcher
this.addSub = function(watcher) {
self.subs.push(watcher);
}
// 通知收集器中所的所有Wathcer,调用其update方法
this.notify = function() {
for (let i = 0; i < self.subs.length; i += 1) {
self.subs[i].update();
}
}
}
const vue = new Vue({
data() {
return {
text: 'hello world'
};
}
})
vue.mount(); // in get
vue._data.text = '123'; // in watcher update /n in get
13、跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
16、前端优化的方法
参考:
http://www.ayqy.net/blog/%E5%89%8D%E7%AB%AF%E4%BC%98%E5%8C%96%EF%BC%9A%E9%9B%85%E8%99%8E35%E6%9D%A1/
JavaScript Basic Memo的更多相关文章
- Javascript Basic Operation Extraction
1. logic operation : '&&' and '||' .For this two logic operations,its' results are inconcl ...
- JavaScript Basic
Exercise-1 Write a JavaScript program to display the current day and time in the following format. T ...
- CSS Basic Memo
1.bootstrap 清除浮动原理 .clearfix:before, .clearfix:after { content: ' ', display: table } .clearfix:afte ...
- Chapter 2 JavaScript Basic
Has 5 primitive types: Undefined, Null, Boolean, String, Number. typeof operator Undefined return u ...
- JavaScript 的基本语法
说明:此类博客来自以下链接,对原内容做了标注重点知识,此处仅供自己学习参考! 来源:https://wangdoc.com/javascript/basic/introduction.html 1. ...
- JavaScript 语言的历史
说明:此类博客来自以下链接,对原内容做了标注重点知识,此处仅供自己学习参考! 来源:https://wangdoc.com/javascript/basic/introduction.html 1.诞 ...
- JavaScript教程——JavaScript 的基本语法(标识符)
标识符 标识符(identifier)指的是用来识别各种值的合法名称.最常见的标识符就是变量名,以及后面要提到的函数名.JavaScript 语言的标识符对大小写敏感,所以a和A是两个不同的标识符. ...
- JavaScript的发展史
一.JavaScript发展历程 1. 诞生 JavaScript因互联网而生,紧跟浏览器的发展而发展. 1990年,欧洲核能研究所(CERN)科学家在互联网(Internet)基础上,发明了 ...
- JavaScript入门-学习笔记(一)
JavaScript入门(一) 学习js之前,我们先来了解一下,什么是JavaScript? JavaScript是一种解释型语言.在运行的时候,一边读一边编译一边执行.简单来说就是,在执行js代码时 ...
随机推荐
- P3440 [POI2006]SZK-Schools(费用流)
P3440 [POI2006]SZK-Schools 每所学校$i$开一个点,$link(S,i,1,0)$ 每个编号$j$开一个点,$link(i,T,1,0)$ 蓝后学校向编号连边,$link(i ...
- selenium + python + nwjs
1.下载chromedriver文件 http://chromedriver.storage.googleapis.com/index.html google官方下载地址 http://dl.nwjs ...
- idea提交git报错Push rejected: Push to origin/master was rejected
参考https://blog.csdn.net/u012934325/article/details/71023241
- Vue学习(一)Vue目录结构
安装教程网上一大把,可以自己搜索.记录下学习过程. 认识下Vue的目录结构,取自:https://www.cnblogs.com/dragonir/p/8711761.html vue 文件目录结构详 ...
- fmt.Sprintf格式化使用
格式化写法: fmt.Sprintf(格式化样式, 参数列表-) 参数说明: 格式化样式:字符串形式,格式化动词以%开头. 参数列表:多个参数以逗号分隔,个数必须与格式化样式中的动词个数对应,否则运行 ...
- Python3学习笔记----生成器
生成器的作用 生成器可以在循环的过程中推算出后续的元素,不必创建完整的list,从而节省大量空间. 生成器的创建 1.把一个列表生成式的 [ ] 改写为( ) # -*- coding: UTF-8 ...
- 1047 Integer Inquiry
String 大数加法模板 #include<stdio.h> #include<string> #include<iostream> using namespac ...
- Oracle创建表、修改字段类型
1.创建表 1.创建表 create table SCM_PER( --SCM_PER表名 ID ) primary key,--主键ID USERID ),--用户ID --Permission v ...
- newnewFoogetName高级面试题
- Caused by : java.io.IOException: Cleartext HTTP traffic to 《“url”》 not permitted
一.问题原因: 根本原因是从Android9.0开始,出于完全因素考虑,默认不再支持http网络请求,需要使用 https. 二.解决方案: 解决的基本思路是:对指定的网址进行过滤,强制允许指定网址继 ...