1。 JavaScript的『预解释』与『变量提高』javascript
先看如下代码输出啥?css
var a= 1; function f() { console.log(a); var a = 2; } f();
首先答案是:undefined;html
因此该题目中:在函数fn的做用域中,首先提取变量声明:var a;由于该做用域中有对a的赋值,因此不会继续查找上级做用域。且在赋值前打印,因此是undefined;vue
相似的看下题:java
var a= 1; function f() { console.log(a); } f(); // 1
var a= 1; function f() { console.log(a); var a; } f(); //undefined
咱们看到,在编译器处理阶段,除了被var
声明的变量会有变量提高这一特性以外,函数也会产生这一特性,可是函数声明与函数表达式两种范式建立的函数却表现出不一样的结果.node
f(); g(); //函数声明 function f() { console.log('f'); } //函数表达式 var g = function() { console.log('g'); };
//fes6
//报错:VM693:2 Uncaught TypeError: g is not a functionsegmentfault
f() 好理解属于函数声明提高;可是对于函数表达式g,被赋予undefined,undefeated没法被执行而报错。浏览器
变量之间冲突app
var a = 3; var a = 4; console.log(a); //4
函数冲突
f(); function f() { console.log('f'); } function f () { console.log('g'); }; // g
3.函数与变量之间冲突
console.log(f); function f() { console.log('f'); } var f ='g';
ƒ f() {
console.log('f');
}
说明函数覆盖了变量;
相似的let,存在暂时性死区:
function f() { console.log(a); let a = 2; } f();
报错: //ReferenceError: a is not defined
这段代码直接报错显示未定义,let
与const
拥有相似的特性,阻止了变量提高,当代码执行到console.log(a)
时,执行换将中a
还从未被定义,所以产生了错误
======================
JS的执行机制:
https://www.cxymsg.com/guide/mechanism.html#javascript%E7%9A%84%E6%89%A7%E8%A1%8C%E7%8E%AF%E5%A2%83
这个问题关键在于两个点,一个是原型对象是什么,另外一个是原型链是如何造成的
绝大部分的函数(少数内建函数除外)都有一个prototype
属性,这个属性是原型对象用来建立新对象实例,而全部被建立的对象都会共享原型对象,所以这些对象即可以访问原型对象的属性。
例如hasOwnProperty()
方法存在于Obejct原型对象中,它即可以被任何对象当作本身的方法使用.
用法:
object.hasOwnProperty( propertyName )
hasOwnProperty()
函数的返回值为Boolean
类型。若是对象object
具备名称为propertyName
的属性,则返回true
,不然返回false
。
var person = { name: "Messi", age: 29, profession: "football player" }; console.log(person.hasOwnProperty("name")); //true console.log(person.hasOwnProperty("hasOwnProperty")); //false console.log(Object.prototype.hasOwnProperty("hasOwnProperty")); //true
由以上代码可知,hasOwnProperty()
并不存在于person
对象中,可是person
依然能够拥有此方法.
因此person
对象是如何找到Object
对象中的方法的呢?靠的是原型链。]
因为js中存在复杂类型和基本类型,对于基本类型而言,是按值传递的.
var a = 1; function test(x) { x = 10; console.log(x); } test(a); console.log(a);
结果是: 10---1
虽然在函数test
中a
被修改,并无有影响到 外部a
的值,基本类型是按值传递的.
咱们将外部a
做为一个对象传入test
函数.
var a = { a: 1, b: 2 }; function test(x) { x.a = 10; console.log(x); } test(a); console.log(a);
// { a: 10, b: 2 }
// { a: 10, b: 2 }
能够看到,在函数体内被修改的a
对象也同时影响到了外部的a
对象,可见复杂类型是按引用传递的.
但是若是再作一个实验:
var a = { a: 1, b: 2 }; function test(x) { x = 10; console.log(x); } test(a); console.log(a);
结果是:
//10
// { a: 1, b: 2 }
外部的a
并无被修改,若是是按引用传递的话,因为共享同一个堆内存,a
在外部也会表现为10
才对. 此时的复杂类型同时表现出了按值传递
和按引用传递
的特性.
复杂类型之因此会产生这种特性,缘由就是在传递过程当中,对象a
先产生了一个副本a
,这个副本a
并非深克隆获得的副本a
,副本a
地址一样指向对象a
指向的堆内存.
所以在函数体中修改x=10
只是修改了副本a
,a
对象没有变化. 可是若是修改了x.a=10
是修改了二者指向的同一堆内存,此时对象a
也会受到影响.
有人讲这种特性叫作传递引用,也有一种说法叫作按共享传递
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。若是同一个值又被赋给另外一个变量,则该值的引用次数加1。相反,若是包含对这个值引用的变量又取得了另一个值,则这个值的引用次数减1
声明一个对象A,每多一个引用,A引用次数+1,每少一个引用,A的引用次数-1
缺点:相互引用的没法消除
当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。内存不能释放。
当变量离开环境时,则将其标记为“离开环境”。释放变量,回收内存。
相似于,函数执行顺序中,各个变量和函数执行在函数调用栈中,此时有标记,当执行完毕以后,退出调用栈,则消除标记,因此垃圾回收机制在必定时间内回收没有标记的变量
==========================
浏览器会『从右往左』解析CSS选择器。
咱们知道DOM Tree与Style Rules合成为 Render Tree,其实是须要将Style Rules附着到DOM Tree上,所以须要根据选择器提供的信息对DOM Tree进行遍历,才能将样式附着到对应的DOM元素上。
如下这段css为例
.mod-nav h3 span {font-size: 16px;}
咱们对应的DOM Tree 以下
若从左向右的匹配,过程是:
若是从右至左的匹配:
后者匹配性能更好,是由于从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点);而从左向右的匹配规则的性能都浪费在了失败的查找上面
『重绘』不必定会出现『重排』,『重排』必然会出现『重绘』
即大小,位置等变化会带来重排;颜色等变化会致使重绘;
任何改变用来构建渲染树的信息都会致使一次重排或重绘:
咱们每每经过改变class的方式来集中改变样式
// 判断是不是黑色系样式 const theme = isDark ? 'dark' : 'light' // 根据判断来设置不一样的class ele.setAttribute('className', theme)
咱们能够经过createDocumentFragment建立一个游离于DOM树以外的节点,而后在此节点上批量操做,最后插入DOM树中,所以只触发一次重排
var fragment = document.createDocumentFragment(); for (let i = 0;i<10;i++){ let node = document.createElement("p"); node.innerHTML = i; fragment.appendChild(node); } document.body.appendChild(fragment);
事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,而后逐级向上传播到较为不具体的节点。
<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <body> <div></div> </body> </html>
若是单击了页面中的<div>
元素,那么这个click事件沿DOM树向上传播,在每一级节点上都会发生,按照以下顺序传播:
事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预约目标以前就捕获它。
仍是以上一节的html结构为例:
在事件捕获过程当中,document对象首先接收到click事件,而后事件沿DOM树依次向下,一直传播到事件的实际目标,即<div>
元素
事件流又称为事件传播,DOM2级事件规定的事件流包括三个阶段:事件捕获阶段(capture phase)、处于目标阶段(target phase)和事件冒泡阶段(bubbling phase)。
触发顺序一般为
事件委托就是利用事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件.
在绑定大量事件的时候每每选择事件委托。
<ul id="parent"> <li class="child">one</li> <li class="child">two</li> <li class="child">three</li> ... </ul> <script type="text/javascript"> //父元素 var dom= document.getElementById('parent'); //父元素绑定事件,代理子元素的点击事件 dom.onclick= function(event) { var event= event || window.event; var curTarget= event.target || event.srcElement; if (curTarget.tagName.toLowerCase() == 'li') { //事件处理 } } </script>
优势:
局限性:
// 模拟 instanceof function instance_of(L, R) { //L 表示左表达式,R 表示右表达式 var O = R.prototype; // 取 R 的显示原型 L = L.__proto__; // 取 L 的隐式原型 while (true) { if (L === null) return false; if (O === L) // 这里重点:当 O 严格等于 L 时,返回 true return true; L = L.__proto__; //L在上面已经等于了其隐式原型,即父级的显示原型,因此这里至关于L往上走了一级 } }
function Person(name, age) { this.name = name; this.age = age; } var person = new Person("Alice", 23);
new一个对象的四个过程:
一、建立一个空对象 var obj = new Object();
二、让Person中的this指向obj,并执行Person的函数体 var result = Person.call(obj);
三、设置原型链,将obj的__proto__成员指向了Person函数对象的prototype成员对象 obj.__proto__ = Person.prototype;
四、判断Person的返回值类型,若是是值类型,返回obj。若是是引用类型,就返回这个引用类型的对象。
if (typeof(result) == "object") person = result; else person = obj;
es5和es6实现类的继承 : http://www.javashuo.com/article/p-mcbrfimx-cx.html
React/Vue不一样组件之间是怎么通讯的?
首先看一下es6中规定的Map的用法:
1,js建立map对象
var map = new Map();
2.将键值对放入map对象
map.set("key",value)
map.set("key1",value1)
map.set("key2",value2)
3.根据key获取map值
map.get(key)
4.删除map指定对象
delete map[key]
5.循环遍历map
map.
forEach(function(key){
console.log("key",key) //输出的是map中的value值
})
------------------
Vue
$dispatch
(已经废除)和$broadcast
(已经废除)React
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <div class="bottom">bottom</div> </body> <script> class EventEmeitter { constructor() { this._events = this._events || new Map(); // 储存事件/回调键值对 this._maxListeners = this._maxListeners || 10; // 设立监听上限 } } // 触发名为type的事件 EventEmeitter.prototype.emit = function (type, ...args) { //type--arson //...args---low-end let handler; // 从储存事件键值对的this._events中获取对应事件回调函数 handler = this._events.get(type); //console.log(handler); if (args.length > 0) { handler.apply(this, args); } else { handler.call(this); } return true; }; // 监听名为type的事件 EventEmeitter.prototype.addListener = function (type, fn) { // 将type事件以及对应的fn函数放入this._events中储存 if (!this._events.get(type)) { this._events.set(type, fn); } }; const emitter = new EventEmeitter(); // 监听一个名为arson的事件对应一个回调函数 emitter.addListener('arson', man => { console.log(`expel ${man}`); }); var bottom = document.querySelector('.bottom'); bottom.addEventListener('click', function () { // 咱们触发arson事件,发现回调成功执行 emitter.emit('arson', 'low-end'); }) </script> </html>