1。 JavaScript的『预解释』与『变量提升』

先看以下代码输出啥?

var a= 1;
function f() {
console.log(a);
var a = 2;
}
f();

首先答案是:undefined;

JavaScript在浏览器中运行的过程分为两个阶段预解释阶段、执行阶段;

  1. 读取var a后,在当前作用域中查找是否有相同声明,如果没有就在当前作用域集合中创建一个名为a的变量,否则忽略此声明继续进行解析;
  2. 接下来,V8引擎会处理a = 2的赋值操作,首先会询问当前作用域中是否有名为a的变量,如果有进行赋值,否则继续向上级作用域询问

所以该题目中:在函数fn的作用域中,首先提取变量声明:var a;因为该作用域中有对a的赋值,所以不会继续查找上级作用域。且在赋值前打印,所以是undefined;

类似的看下题:

var a= 1;
function f() {
console.log(a);
}
f(); // 1
var a= 1;
function f() {
console.log(a);
var a;
}
f(); //undefined

函数声明与函数表达式

我们看到,在编译器处理阶段,除了被var声明的变量会有变量提升这一特性之外,函数也会产生这一特性,但是函数声明与函数表达式两种范式创建的函数却表现出不同的结果.

f();
g();
//函数声明
function f() {
console.log('f');
}
//函数表达式
var g = function() {
console.log('g');
};

//f

//报错:VM693:2 Uncaught TypeError: g is not a function

f() 好理解属于函数声明提升;但是对于函数表达式g,被赋予undefined,undefeated无法被执行而报错。

冲突处理

变量之间冲突

var a = 3;
var a = 4;
console.log(a); //

函数冲突

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

这段代码直接报错显示未定义,letconst拥有类似的特性,阻止了变量提升,当代码执行到console.log(a)时,执行换将中a还从未被定义,因此产生了错误

======================

JS的执行机制:

浅析JS中的堆内存与栈内存

浅析javascript调用栈

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对象中的方法的呢?靠的是原型链。]

JavaScript的参数是按照什么方式传递的?

基本类型传递方式

由于js中存在复杂类型和基本类型,对于基本类型而言,是按值传递的.

var a = 1;
function test(x) {
x = 10;
console.log(x);
}
test(a);
console.log(a);

结果是: 10---1

虽然在函数testa被修改,并没有有影响到 外部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也会受到影响.

有人讲这种特性叫做传递引用,也有一种说法叫做按共享传递

简述JavaScript的垃圾回收机制

引用计次

当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1

声明一个对象A,每多一个引用,A引用次数+1,每少一个引用,A的引用次数-1

缺点:相互引用的无法消除

标记清除

当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。内存不能释放。
当变量离开环境时,则将其标记为“离开环境”。释放变量,回收内存。

类似于,函数执行顺序中,各个变量和函数执行在函数调用栈中,此时有标记,当执行完毕之后,退出调用栈,则消除标记,所以垃圾回收机制在一定时间内回收没有标记的变量

浅析javascript调用栈

==========================

浏览器如何解析css选择器

浏览器会『从右往左』解析CSS选择器。

我们知道DOM Tree与Style Rules合成为 Render Tree,实际上是需要将Style Rules附着到DOM Tree上,因此需要根据选择器提供的信息对DOM Tree进行遍历,才能将样式附着到对应的DOM元素上。

以下这段css为例

.mod-nav h3 span {font-size: 16px;}

我们对应的DOM Tree 如下

若从左向右的匹配,过程是:

  1. 从 .mod-nav 开始,遍历子节点 header 和子节点 div
  2. 然后各自向子节点遍历。在右侧 div 的分支中
  3. 最后遍历到叶子节点 a ,发现不符合规则,需要回溯到 ul 节点,再遍历下一个 li-a,一颗DOM树的节点动不动上千,这种效率很低。

如果从右至左的匹配:

  1. 先找到所有的最右节点 span,对于每一个 span,向上寻找节点 h3
  2. 由 h3再向上寻找 class="mod-nav" 的节点
  3. 最后找到根元素 html 则结束这个分支的遍历。

后者匹配性能更好,是因为从右向左的匹配在第一步就筛选掉了大量的不符合条件的最右节点(叶子节点);而从左向右的匹配规则的性能都浪费在了失败的查找上面

浏览器重绘与重排的区别?

  • 重排: 部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算,表现为重新生成布局,重新排列元素
  • 重绘: 由于节点的几何属性发生改变或者由于样式发生改变,例如改变元素背景色时,屏幕上的部分内容需要更新,表现为某些元素的外观被改变

『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』

即大小,位置等变化会带来重排;颜色等变化会导致重绘;

如何触发重排和重绘?

任何改变用来构建渲染树的信息都会导致一次重排或重绘:

  • 添加、删除、更新DOM节点
  • 通过display: none隐藏一个DOM节点-触发重排和重绘
  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
  • 移动或者给页面中的DOM节点添加动画
  • 添加一个样式表,调整样式属性
  • 用户行为,例如调整窗口大小,改变字号,或者滚动。

#如何避免重绘或者重排?

#集中改变样式

我们往往通过改变class的方式来集中改变样式

 

// 判断是否是黑色系样式
const theme = isDark ? 'dark' : 'light' // 根据判断来设置不同的class
ele.setAttribute('className', theme)

使用DocumentFragment

我们可以通过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);

DOM的事件流是什么

事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点。

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<body>
<div></div>
</body>
</html>

如果单击了页面中的<div>元素,那么这个click事件沿DOM树向上传播,在每一级节点上都会发生,按照如下顺序传播:

  1. div
  2. body
  3. html
  4. document

#事件捕获

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前就捕获它。

还是以上一节的html结构为例:

在事件捕获过程中,document对象首先接收到click事件,然后事件沿DOM树依次向下,一直传播到事件的实际目标,即<div>元素

  1. document
  2. html
  3. body
  4. div

事件流又称为事件传播,DOM2级事件规定的事件流包括三个阶段:事件捕获阶段(capture phase)、处于目标阶段(target phase)和事件冒泡阶段(bubbling phase)。

触发顺序通常为

  1. 进行事件捕获,为截获事件提供了机会
  2. 实际的目标接收到事件
  3. 冒泡阶段,可以在这个阶段对事件做出响应

什么是事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件.

在绑定大量事件的时候往往选择事件委托。

<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>

优点:

  • 节省内存占用,减少事件注册
  • 新增子对象时无需再次对其绑定事件,适合动态添加元素

局限性:

  • focus、blur 之类的事件本身没有事件冒泡机制,所以无法委托
  • mousemove、mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,不适合事件委托

vue中央事件总线eventBus的简单理解和使用

实现instanceOf

// 模拟 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往上走了一级
}
}

js中new一个对象的过程

function Person(name, age) {
this.name = name;
this.age = age;
}
var person = new Person("Alice", 23);

new一个对象的四个过程:

1、创建一个空对象  var obj = new Object();

2、让Person中的this指向obj,并执行Person的函数体  var result = Person.call(obj);

3、设置原型链,将obj的__proto__成员指向了Person函数对象的prototype成员对象  obj.__proto__ = Person.prototype;

4、判断Person的返回值类型,如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。

if (typeof(result) == "object")
person = result;
else
person = obj;

es5和es6实现类的继承    : https://www.cnblogs.com/xiaozhumaopao/p/11624344.html

如何实现一个Event

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

  1. 父子组件用Props通信
  2. 非父子组件用Event Bus通信
  3. 如果项目够复杂,可能需要Vuex等全局状态管理库通信
  4. $dispatch(已经废除)和$broadcast(已经废除)

React

  1. 父子组件,父->子直接用Props,子->父用callback回调
  2. 非父子组件,用发布订阅模式的Event模块
  3. 项目复杂的话用Redux、Mobx等全局状态管理管库
  4. 用新的Context Api
<!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>

前端知识总结--2 js部分的更多相关文章

  1. 前端知识复习: JS选中变色

    前端知识复习:JS选中变色 上篇文章 :前端知识复习:Html DIV 图文混排(文字放在图片下边) Js选中图片效果 <!DOCTYPE html> <html xmlns=&qu ...

  2. 前端知识(二)08-Vue.js的路由-谷粒学院

    目录 一.锚点的概念 二.路由的作用 三.路由实例 1.复制js资源 2.创建 路由.html 3.引入js 4.编写html 5.编写js 一.锚点的概念 案例:百度百科 特点:单页Web应用,预先 ...

  3. 前端知识复习:Html DIV 图文混排(文字放在图片下边)

    Html知识复习之图文混排 练习练习基础 先上效果图: 废话不多说,直接贴代码: <!DOCTYPE html> <html xmlns="http://www.w3.or ...

  4. 3 HTML&JS等前端知识系列之javascript的基础

    preface 作为一名运维开发,必须懂得前端知识,比如javascript,dom等等,下面就聊聊javascript. include 数据格式 条件判断,循环流程等. 函数 面向对象 what ...

  5. JS前端知识模块大全

    公司前端:小胖提供,表示感谢 1. 基础 HTML, CSS, JS 文档 W3CSCHOOL: http://www.w3schools.com/ MDN: https://developer.mo ...

  6. 移动端 Web 开发前端知识整理

    文章来源: http://www.restran.net/2015/05/14/mobile-web-front-end-collections/ 最近整理的移动端 Web 开发前端知识,不定期更新. ...

  7. 1. web前端开发分享-css,js入门篇

    关注前端这么多年,没有大的成就,就入门期间积累了不少技巧与心得,跟大家分享一下,不一定都适合每个人,毕竟人与人的教育背景与成长环境心理活动都有差别,但就别人的心得再结合自己的特点,然后探索适合自己的学 ...

  8. web前端知识体系总结

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

  9. 自己总结的web前端知识体系大全【欢迎补充】

    1. 前言 大约在几个月之前,让我看完了<webkit技术内幕>这本书的时候,突然有了一个想法.想把整个web前端开发所需要的知识都之中在一个视图中,形成一个完整的web前端知识体系,目的 ...

随机推荐

  1. SecureCRT日志优化

    SecureCRT日志优化 用了这么多ssh软件,但对secureCRT情有独钟.今天来对做一下对SecureCRT的优化 一.手动模式: 选择"File"->"L ...

  2. ReentrantLock 非公平锁不公平在哪

    重入锁关键地带: 1:使用unsafe的cas方式对AQS中的state成员变量进行“原子加一”操作. 2:如果当前线程多次lock,相当于对state在原有值基础上继续加一操作:释放锁的条件为“原子 ...

  3. linux svn开机自动启动服务

    SVN设置开机自动启动 usr/lib/systemd/system/添加svn.service文件 home/sdbdatasvn/svnrepos(换成绝对路径) 如果出现权限问题,请chmod  ...

  4. springboot注入的四个注解

    java配置主要靠java类和一些注解来达到和xml配置一样的效果,比较常用的注解有: @Configuration:声明一个类作为配置类,代替xml文件@Bean:声明在方法上,将方法的返回值加入B ...

  5. Integer.highestOneBit(int i)方法的作用与底层实现

    在Integer类中有这么一个方法,你可以给它传入一个数字,它将返回小于等于这个数字的一个2的幂次方数.这个方法就是highestOneBit(int i). 比如下面的Demo,注意方法的输入与返回 ...

  6. json转义问题

    后端程序接受前台传递过来json 1正常json没有问题 比如  {"id":21,"userName":"2张天师","phon ...

  7. html input复选框的checked属性

    input --checked: 只要复选框有checked属性,不管属性值为空或者为true or false或任意值,复选框都会被选中.切忌:checked属性值不要带引号 <input t ...

  8. Pycharm下直接升级库所遇到的'main'问题

    Pycharm下直接升级库所遇到的pip模块中无'main'问题 Author : Benjamin142857 Date : 8/19/2018 对于Pycharm中直接升级库,只需在 \(Sett ...

  9. vue页面params传值的必须传name

    a.vue向b.vue传值 a.vue this.$router.push({ path: '/payType', query: { putUpList: this.putUpList, name:' ...

  10. CCF 2016-12-1 最大波动

    CCF 2016-12-1 最大波动 题目 问题描述 小明正在利用股票的波动程度来研究股票.小明拿到了一只股票每天收盘时的价格,他想知道,这只股票连续几天的最大波动值是多少,即在这几天中某天收盘价格与 ...