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代码时 ...
随机推荐
- Yii1操作phpexcel
Yii::import('application.vendors.phpexcel.*'); Yii::import('application.vendors.phpexcel.PHPExcel.*' ...
- Axure RP 9 Mac正式汉化版安装教程
Axure RP9 汉化版是mac平台上一款交互式原型设计神器,是专为UX专业人员和业务分析师设计的专业网站原型设计工具!可以帮助他们快速创建应用程序和网站的线框,原型和规格!新功能包括一系列广泛的改 ...
- python面对对象
面对过程VS面对对象 面向过程的程序设计的核心是过程,过程就是解决问题的步骤,面向过程的设计就是考虑周全什么时候处理什么东西 优点:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可. ...
- MapReduce实现PageRank算法(邻接矩阵法)
前言 之前写过稀疏图的实现方法,这次写用矩阵存储数据的算法实现,只要会矩阵相乘的话,实现这个就很简单了.如果有不懂的可以先看一下下面两篇随笔. MapReduce实现PageRank算法(稀疏图法) ...
- c# linq lambda 去重,排序,取最高纪录。
----------------------------------------------------.对基础类型排序 方法一: 调用sort方法,如果需要降序,进行反转: List<int& ...
- python-Word模板填充-docxtpl
docxtpl 按指定的word模板填充内容 安装 pip install docxtpl 示例 from docxtpl import DocxTemplate data_dic = { 't1': ...
- VS 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
在VS中编译 C 语言项目,如果使用了 scanf 函数,编译时便会提示如下错误: error C4996: 'scanf': This function or variable may be uns ...
- 数据库-left join,right join,inner join,full join
2019-04-18 22:36:26 sql中的连接查询有inner join(内连接).left join(左连接).right join(右连接).full join(全连接)四种方式,它们之间 ...
- Kotlin 随笔小计
最近准备学Kotlin 现在Kotlin也能支持IOS开发了,准备后面买个Mac也能进行IOS开发 当然目标还是看着能不能把一些小的Android项目重构下 也算是定个目标吧,由于沉迷吃鸡,日志都没怎 ...
- 关于oracle数据库中获取版本号类数据最大值的sql
目前还在高度加班中,但是本次内容怕自己忘记,好不容易解决的,所以赶紧先随便抽点时间记录下,也没来得及考虑效率什么的优化问题,免得以后忘记了. 测试库结构如下: 表名为 testtab 字段名为test ...