组件之间通信
- 父子组件通信
- 自定义事件
- redux和context
context如何使用
- 父组件向其下所有子孙组件传递信息
- 如一些简略的信息:主题、语言
- 简单的公共信息用redux
在跨层级通信中,次要分为一层或多层的状况
- 如果只有一层,那么依照 React 的树形构造进行分类的话,次要有以下三种状况:
父组件向子组件通信
,子组件向父组件通信
以及平级的兄弟组件间相互通信
。 - 在父与子的状况下 ,因为 React 的设计实际上就是传递
Props
即可。那么场景体现在容器组件与展现组件之间,通过Props
传递state
,让展现组件受控。 - 在子与父的状况下 ,有两种形式,别离是回调函数与实例函数。回调函数,比方输入框向父级组件返回输出内容,按钮向父级组件传递点击事件等。实例函数的状况有些特地,次要是在父组件中
通过 React 的 ref API 获取子组件的实例
,而后是通过实例调用子组件的实例函数
。这种形式在过来常见于 Modal 框的显示与暗藏 多层级间的数据通信,有两种状况 。第一种是一个容器中蕴含了多层子组件,须要最底部的子组件与顶部组件进行通信。在这种状况下,如果一直透传 Props 或回调函数,不仅代码层级太深,后续也很不好保护。第二种是两个组件不相干,在整个 React 的组件树的两侧,齐全不相交。那么基于多层级间的通信个别有三个计划。
- 第一个是应用 React 的
Context API
,最常见的用处是做语言包国际化 - 第二个是应用全局变量与事件。
- 第三个是应用状态治理框架,比方 Flux、Redux 及 Mobx。长处是因为引入了状态治理,使得我的项目的开发模式与代码构造得以束缚,毛病是学习老本绝对较高
- 第一个是应用 React 的
代码输入后果
function runAsync (x) { const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000)) return p}Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))
输入后果如下:
123[1, 2, 3]
首先,定义了一个Promise,来异步执行函数runAsync,该函数传入一个值x,而后距离一秒后打印出这个x。
之后再应用Promise.all
来执行这个函数,执行的时候,看到一秒之后输入了1,2,3,同时输入了数组[1, 2, 3],三个函数是同步执行的,并且在一个回调函数中返回了所有的后果。并且后果和函数的执行程序是统一的。
过程和线程的区别
- 过程能够看做独立利用,线程不能
- 资源:过程是cpu资源分配的最小单位(是能领有资源和独立运行的最小单位);线程是cpu调度的最小单位(线程是建设在过程的根底上的一次程序运行单位,一个过程中能够有多个线程)。
- 通信方面:线程间能够通过间接共享同一过程中的资源,而过程通信须要借助 过程间通信。
- 调度:过程切换比线程切换的开销要大。线程是CPU调度的根本单位,线程的切换不会引起过程切换,但某个过程中的线程切换到另一个过程中的线程时,会引起过程切换。
- 零碎开销:因为创立或撤销过程时,零碎都要为之调配或回收资源,如内存、I/O 等,其开销远大于创立或撤销线程时的开销。同理,在进行过程切换时,波及以后执行过程 CPU 环境还有各种各样状态的保留及新调度过程状态的设置,而线程切换时只需保留和设置大量寄存器内容,开销较小。
深拷贝
实现一:不思考 Symbol
function deepClone(obj) { if(!isObject(obj)) return obj; let newObj = Array.isArray(obj) ? [] : {}; // for...in 只会遍历对象本身的和继承的可枚举的属性(不含 Symbol 属性) for(let key in obj) { // obj.hasOwnProperty() 办法只思考对象本身的属性 if(obj.hasOwnProperty(key)) { newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]; } } return newObj;}
实现二:思考 Symbol
// hash 作为一个查看器,防止对象深拷贝中呈现环援用,导致爆栈function deepClone(obj, hash = new WeakMap()) { if(!isObject(obj)) return obj; // 查看是有存在雷同的对象在之前拷贝过,有则返回之前拷贝后存于hash中的对象 if(hash.has(obj)) return hash.get(obj); let newObj = Array.isArray(obj) ? [] : {}; // 备份存在hash中,newObj目前是空对象、数组。前面会对属性进行追加,这里存的值是对象的栈 hash.set(obj, newObj); // Reflect.ownKeys返回一个数组,蕴含对象本身的(不含继承的)所有键名,不论键名是 Symbol 或字符串,也不论是否可枚举。 Reflect.ownKeys(obj).forEach(key => { // 属性值如果是对象,则进行递归深拷贝,否则间接拷贝 newObj[key] = isObject(obj[key]) ? deepClone(obj[key], hash) : obj[key]; }); return newObj;}
Vue的生命周期是什么 每个钩子外面具体做了什么事件
Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是Vue的⽣命周期。1、beforeCreate(创立前) :数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到data、computed、watch、methods上的办法和数据。2、created(创立后) :实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到 `$el` 属性。3、beforeMount(挂载前) :在挂载开始之前被调用,相干的render函数首次被调用。实例已实现以下的配置:编译模板,把data外面的数据和模板生成html。此时还没有挂载html到页面上。4、mounted(挂载后) :在el被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的html内容替换el属性指向的DOM对象。实现模板中的html渲染到html 页面中。此过程中进行ajax交互。5、beforeUpdate(更新前) :响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。6、updated(更新后):在因为数据更改导致的虚构DOM从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM曾经更新,所以能够执行依赖于DOM的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。7、beforeDestroy(销毁前) :实例销毁之前调用。这一步,实例依然齐全可用,`this` 仍能获取到实例。8、destroyed(销毁后) :实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。另外还有 `keep-alive` 独有的生命周期,别离为 `activated` 和 `deactivated` 。用 `keep-alive` 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 `deactivated` 钩子函数,命中缓存渲染后会执行 `activated` 钩子函数。
IndexedDB有哪些特点?
IndexedDB 具备以下特点:
- 键值对贮存:IndexedDB 外部采纳对象仓库(object store)存放数据。所有类型的数据都能够间接存入,包含 JavaScript 对象。对象仓库中,数据以"键值对"的模式保留,每一个数据记录都有对应的主键,主键是举世无双的,不能有反复,否则会抛出一个谬误。
- 异步:IndexedDB 操作时不会锁死浏览器,用户仍然能够进行其余操作,这与 LocalStorage 造成比照,后者的操作是同步的。异步设计是为了避免大量数据的读写,拖慢网页的体现。
- 反对事务:IndexedDB 反对事务(transaction),这意味着一系列操作步骤之中,只有有一步失败,整个事务就都勾销,数据库回滚到事务产生之前的状态,不存在只改写一部分数据的状况。
- 同源限度: IndexedDB 受到同源限度,每一个数据库对应创立它的域名。网页只能拜访本身域名下的数据库,而不能拜访跨域的数据库。
- 贮存空间大:IndexedDB 的贮存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有下限。
- 反对二进制贮存:IndexedDB 不仅能够贮存字符串,还能够贮存二进制数据(ArrayBuffer 对象和 Blob 对象)。
AJAX
const getJSON = function(url) { return new Promise((resolve, reject) => { const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); xhr.open('GET', url, false); xhr.setRequestHeader('Accept', 'application/json'); xhr.onreadystatechange = function() { if (xhr.readyState !== 4) return; if (xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } } xhr.send(); })}
实现数组原型办法
forEach
Array.prototype.forEach2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) // this 就是以后的数组 const len = O.length >>> 0 // 前面有解释 let k = 0 while (k < len) { if (k in O) { callback.call(thisArg, O[k], k, O); } k++; }}
O.length >>> 0 是什么操作?就是无符号右移 0 位,那有什么意义嘛?就是为了保障转换后的值为正整数。其实底层做了 2 层转换,第一是非 number 转成 number 类型,第二是将 number 转成 Uint32 类型
map
基于 forEach 的实现可能很容易写出 map 的实现:
- Array.prototype.forEach2 = function(callback, thisArg) {+ Array.prototype.map2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >>> 0- let k = 0+ let k = 0, res = [] while (k < len) { if (k in O) {- callback.call(thisArg, O[k], k, O);+ res[k] = callback.call(thisArg, O[k], k, O); } k++; }+ return res}
filter
同样,基于 forEach 的实现可能很容易写出 filter 的实现:
- Array.prototype.forEach2 = function(callback, thisArg) {+ Array.prototype.filter2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >>> 0- let k = 0+ let k = 0, res = [] while (k < len) { if (k in O) {- callback.call(thisArg, O[k], k, O);+ if (callback.call(thisArg, O[k], k, O)) {+ res.push(O[k]) + } } k++; }+ return res}
some
同样,基于 forEach 的实现可能很容易写出 some 的实现:
- Array.prototype.forEach2 = function(callback, thisArg) {+ Array.prototype.some2 = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >>> 0 let k = 0 while (k < len) { if (k in O) {- callback.call(thisArg, O[k], k, O);+ if (callback.call(thisArg, O[k], k, O)) {+ return true+ } } k++; }+ return false}
reduce
Array.prototype.reduce2 = function(callback, initialValue) { if (this == null) { throw new TypeError('this is null or not defined') } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function') } const O = Object(this) const len = O.length >>> 0 let k = 0, acc if (arguments.length > 1) { acc = initialValue } else { // 没传入初始值的时候,取数组中第一个非 empty 的值为初始值 while (k < len && !(k in O)) { k++ } if (k > len) { throw new TypeError( 'Reduce of empty array with no initial value' ); } acc = O[k++] } while (k < len) { if (k in O) { acc = callback(acc, O[k], k, O) } k++ } return acc}
Promise.all
形容:所有 promise
的状态都变成 fulfilled
,就会返回一个状态为 fulfilled
的数组(所有promise
的 value
)。只有有一个失败,就返回第一个状态为 rejected
的 promise
实例的 reason
。
实现:
Promise.all = function(promises) { return new Promise((resolve, reject) => { if(Array.isArray(promises)) { if(promises.length === 0) return resolve(promises); let result = []; let count = 0; promises.forEach((item, index) => { Promise.resolve(item).then( value => { count++; result[index] = value; if(count === promises.length) resolve(result); }, reason => reject(reason) ); }) } else return reject(new TypeError("Argument is not iterable")); });}
一个 tcp 连贯能发几个 http 申请?
如果是 HTTP 1.0 版本协定,个别状况下,不反对长连贯,因而在每次申请发送结束之后,TCP 连贯即会断开,因而一个 TCP 发送一个 HTTP 申请,然而有一种状况能够将一条 TCP 连贯放弃在沉闷状态,那就是通过 Connection 和 Keep-Alive 首部,在申请头带上 Connection: Keep-Alive,并且能够通过 Keep-Alive 通用首部中指定的,用逗号分隔的选项调节 keep-alive 的行为,如果客户端和服务端都反对,那么其实也能够发送多条,不过此形式也有限度,能够关注《HTTP 权威指南》4.5.5 节对于 Keep-Alive 连贯的限度和规定。
而如果是 HTTP 1.1 版本协定,反对了长连贯,因而只有 TCP 连接不断开,便能够始终发送 HTTP 申请,继续一直,没有下限; 同样,如果是 HTTP 2.0 版本协定,反对多用复用,一个 TCP 连贯是能够并发多个 HTTP 申请的,同样也是反对长连贯,因而只有一直开 TCP 的连贯,HTTP 申请数也是能够没有下限地继续发送
Promise.any
形容:只有 promises
中有一个fulfilled
,就返回第一个fulfilled
的Promise
实例的返回值。
实现
Promise.any = function(promises) { return new Promise((resolve, reject) => { if(Array.isArray(promises)) { if(promises.length === 0) return reject(new AggregateError("All promises were rejected")); let count = 0; promises.forEach((item, index) => { Promise.resolve(item).then( value => resolve(value), reason => { count++; if(count === promises.length) { reject(new AggregateError("All promises were rejected")); }; } ); }) } else return reject(new TypeError("Argument is not iterable")); });}
具体阐明 Event loop
家喻户晓 JS 是门非阻塞单线程语言,因为在最后 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,咱们在多个线程中解决 DOM 就可能会产生问题(一个线程中新加节点,另一个线程中删除节点),当然能够引入读写锁解决这个问题。
JS 在执行的过程中会产生执行环境,这些执行环境会被程序的退出到执行栈中。如果遇到异步的代码,会被挂起并退出到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出须要执行的代码并放入执行栈中执行,所以实质上来说 JS 中的异步还是同步行为。
console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);console.log('script end');
以上代码尽管 setTimeout
延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,有余会主动减少。所以 setTimeout
还是会在 script end
之后打印。
不同的工作源会被调配到不同的 Task 队列中,工作源能够分为 微工作(microtask) 和 宏工作(macrotask)。在 ES6 标准中,microtask 称为 jobs
,macrotask 称为 task
。
console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);new Promise((resolve) => { console.log('Promise') resolve()}).then(function() { console.log('promise1');}).then(function() { console.log('promise2');});console.log('script end');// script start => Promise => script end => promise1 => promise2 => setTimeout
以上代码尽管 setTimeout
写在 Promise
之前,然而因为 Promise
属于微工作而 setTimeout
属于宏工作,所以会有以上的打印。
微工作包含 process.nextTick
,promise
,Object.observe
,MutationObserver
宏工作包含 script
, setTimeout
,setInterval
,setImmediate
,I/O
,UI rendering
很多人有个误区,认为微工作快于宏工作,其实是谬误的。因为宏工作中包含了 script
,浏览器会先执行一个宏工作,接下来有异步代码的话就先执行微工作。
所以正确的一次 Event loop 程序是这样的
- 执行同步代码,这属于宏工作
- 执行栈为空,查问是否有微工作须要执行
- 执行所有微工作
- 必要的话渲染 UI
- 而后开始下一轮 Event loop,执行宏工作中的异步代码
通过上述的 Event loop 程序可知,如果宏工作中的异步代码有大量的计算并且须要操作 DOM 的话,为了更快的 界面响应,咱们能够把操作 DOM 放入微工作中。
Node 中的 Event loop
Node 中的 Event loop 和浏览器中的不雷同。
Node 的 Event loop 分为6个阶段,它们会依照程序重复运行
┌───────────────────────┐┌─>│ timers ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ I/O callbacks ││ └──────────┬────────────┘│ ┌──────────┴────────────┐│ │ idle, prepare ││ └──────────┬────────────┘ ┌───────────────┐│ ┌──────────┴────────────┐ │ incoming: ││ │ poll │<──connections─── ││ └──────────┬────────────┘ │ data, etc. ││ ┌──────────┴────────────┐ └───────────────┘│ │ check ││ └──────────┬────────────┘│ ┌──────────┴────────────┐└──┤ close callbacks │ └───────────────────────┘
timer
timers 阶段会执行 setTimeout
和 setInterval
一个 timer
指定的工夫并不是精确工夫,而是在达到这个工夫后尽快执行回调,可能会因为零碎正在执行别的事务而提早。
上限的工夫有一个范畴:[1, 2147483647]
,如果设定的工夫不在这个范畴,将被设置为1。
I/O
I/O 阶段会执行除了 close 事件,定时器和 setImmediate
的回调
idle, prepare
idle, prepare 阶段外部实现
poll
poll 阶段很重要,这一阶段中,零碎会做两件事件
- 执行到点的定时器
- 执行 poll 队列中的事件
并且当 poll 中没有定时器的状况下,会发现以下两件事件
- 如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者零碎限度
如果 poll 队列为空,会有两件事产生
- 如果有
setImmediate
须要执行,poll 阶段会进行并且进入到 check 阶段执行setImmediate
- 如果没有
setImmediate
须要执行,会期待回调被退出到队列中并立刻执行回调
- 如果有
如果有别的定时器须要被执行,会回到 timer 阶段执行回调。
check
check 阶段执行 setImmediate
close callbacks
close callbacks 阶段执行 close 事件
并且在 Node 中,有些状况下的定时器执行程序是随机的
setTimeout(() => { console.log('setTimeout');}, 0);setImmediate(() => { console.log('setImmediate');})// 这里可能会输入 setTimeout,setImmediate// 可能也会相同的输入,这取决于性能// 因为可能进入 event loop 用了不到 1 毫秒,这时候会执行 setImmediate// 否则会执行 setTimeout
当然在这种状况下,执行程序是雷同的
var fs = require('fs')fs.readFile(__filename, () => { setTimeout(() => { console.log('timeout'); }, 0); setImmediate(() => { console.log('immediate'); });});// 因为 readFile 的回调在 poll 中执行// 发现有 setImmediate ,所以会立刻跳到 check 阶段执行回调// 再去 timer 阶段执行 setTimeout// 所以以上输入肯定是 setImmediate,setTimeout
下面介绍的都是 macrotask 的执行状况,microtask 会在以上每个阶段实现后立刻执行。
setTimeout(()=>{ console.log('timer1') Promise.resolve().then(function() { console.log('promise1') })}, 0)setTimeout(()=>{ console.log('timer2') Promise.resolve().then(function() { console.log('promise2') })}, 0)// 以上代码在浏览器和 node 中打印状况是不同的// 浏览器中打印 timer1, promise1, timer2, promise2// node 中打印 timer1, timer2, promise1, promise2
Node 中的 process.nextTick
会先于其余 microtask 执行。
setTimeout(() => { console.log("timer1"); Promise.resolve().then(function() { console.log("promise1"); });}, 0);process.nextTick(() => { console.log("nextTick");});// nextTick, timer1, promise1
对浏览器内核的了解
浏览器内核次要分成两局部:
- 渲染引擎的职责就是渲染,即在浏览器窗口中显示所申请的内容。默认状况下,渲染引擎能够显示 html、xml 文档及图片,它也能够借助插件显示其余类型数据,例如应用 PDF 阅读器插件,能够显示 PDF 格局。
- JS 引擎:解析和执行 javascript 来实现网页的动态效果。
最开始渲染引擎和 JS 引擎并没有辨别的很明确,起初 JS 引擎越来越独立,内核就偏向于只指渲染引擎。
插入排序--工夫复杂度 n^2
题目形容:实现一个插入排序
实现代码如下:
function insertSort(arr) { for (let i = 1; i < arr.length; i++) { let j = i; let target = arr[j]; while (j > 0 && arr[j - 1] > target) { arr[j] = arr[j - 1]; j--; } arr[j] = target; } return arr;}// console.log(insertSort([3, 6, 2, 4, 1]));
AJAX
实现:利用 XMLHttpRequest
// getconst getJSON = (url) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); // open 办法用于指定 HTTP 申请的参数: method, url, async(是否异步,默认true) xhr.open("GET", url, false); xhr.setRequestHeader('Content-Type', 'application/json'); // onreadystatechange 属性指向一个监听函数。 // readystatechange 事件产生时(实例的readyState属性变动),就会执行这个属性。 xhr.onreadystatechange = function(){ // 4 示意服务器返回的数据曾经齐全接管,或者本次接管曾经失败 if(xhr.readyState !== 4) return; // 申请胜利,基本上只有2xx和304的状态码,示意服务器返回是失常状态 if(xhr.status === 200 || xhr.status === 304) { // responseText 属性返回从服务器接管到的字符串 resolve(xhr.responseText); } // 申请失败 else { reject(new Error(xhr.responseText)); } } xhr.send(); });}// postconst postJSON = (url, data) => { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.open("POST", url); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = function(){ if(xhr.readyState !== 4) return; if(xhr.status === 200 || xhr.status === 304) { resolve(xhr.responseText); } else { reject(new Error(xhr.responseText)); } } xhr.send(data); });}
Promise.race
形容:只有promises
中有一个率先扭转状态,就返回这个率先扭转的Promise
实例的返回值。
实现:
Promise.race = function(promises){ return new Promise((resolve, reject) => { if(Array.isArray(promises)) { if(promises.length === 0) return resolve(promises); promises.forEach((item) => { Promise.resolve(item).then( value => resolve(value), reason => reject(reason) ); }) } else return reject(new TypeError("Argument is not iterable")); });}
单例模式
用意:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点。
次要解决:一个全局应用的类频繁地创立与销毁。
何时应用:当您想管制实例数目,节俭系统资源的时候。
如何解决:判断零碎是否曾经有这个单例,如果有则返回,如果没有则创立。
实现:
var Singleton = (function() { // 如果在外部申明 SingletonClass 对象,则无奈在内部间接调用 var SingletonClass = function() { }; var instance; return function() { // 如果已存在,则返回 instance if(instance) return instance; // 如果不存在,则new 一个 SingletonClass 对象 instance = new SingletonClass(); return instance; }})();// 测试var a = new Singleton();var b = new Singleton();console.log(a === b); // true
代码输入问题
function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = { demo: 5 }; this.show = function () { console.log(this.a , this.b , this.c.demo ); }}function Child() { this.a = 2; this.change = function () { this.b.push(this.a); this.a = this.b.length; this.c.demo = this.a++; }}Child.prototype = new Parent();var parent = new Parent();var child1 = new Child();var child2 = new Child();child1.a = 11;child2.a = 12;parent.show();child1.show();child2.show();child1.change();child2.change();parent.show();child1.show();child2.show();
输入后果:
parent.show(); // 1 [1,2,1] 5child1.show(); // 11 [1,2,1] 5child2.show(); // 12 [1,2,1] 5parent.show(); // 1 [1,2,1] 5child1.show(); // 5 [1,2,1,11,12] 5child2.show(); // 6 [1,2,1,11,12] 5
这道题目值得神帝,他波及到的知识点很多,例如this的指向、原型、原型链、类的继承、数据类型等。
解析:
- parent.show(),能够间接取得所需的值,没啥好说的;
- child1.show(),
Child
的构造函数本来是指向Child
的,题目显式将Child
类的原型对象指向了Parent
类的一个实例,须要留神Child.prototype
指向的是Parent
的实例parent
,而不是指向Parent
这个类。 - child2.show(),这个也没啥好说的;
- parent.show(),
parent
是一个Parent
类的实例,Child.prorotype
指向的是Parent
类的另一个实例,两者在堆内存中互不影响,所以上述操作不影响parent
实例,所以输入后果不变; - child1.show(),
child1
执行了change()
办法后,产生了怎么的变动呢? - this.b.push(this.a),因为this的动静指向个性,this.b会指向
Child.prototype
上的b数组,this.a会指向child1
的a属性,所以Child.prototype.b
变成了[1,2,1,11]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child1.a
变为4; - this.c.demo = this.a++,因为
child1
本身属性并没有c这个属性,所以此处的this.c
会指向Child.prototype.c
,this.a
值为4,为原始类型,故赋值操作时会间接赋值,Child.prototype.c.demo
的后果为4,而this.a
随后自增为5(4 + 1 = 5)。 child2
执行了change()
办法, 而child2
和child1
均是Child
类的实例,所以他们的原型链指向同一个原型对象Child.prototype
,也就是同一个parent
实例,所以child2.change()
中所有影响到原型对象的语句都会影响child1
的最终输入后果。- this.b.push(this.a),因为this的动静指向个性,this.b会指向
Child.prototype
上的b数组,this.a会指向child2
的a属性,所以Child.prototype.b
变成了[1,2,1,11,12]; - this.a = this.b.length,这条语句中
this.a
和this.b
的指向与上一句统一,故后果为child2.a
变为5; - this.c.demo = this.a++,因为
child2
本身属性并没有c这个属性,所以此处的this.c
会指向Child.prototype.c
,故执行后果为Child.prototype.c.demo
的值变为child2.a
的值5,而child2.a
最终自增为6(5 + 1 = 6)。
代码输入后果
var obj = { say: function() { var f1 = () => { console.log("1111", this); } f1(); }, pro: { getPro:() => { console.log(this); } }}var o = obj.say;o();obj.say();obj.pro.getPro();
输入后果:
1111 window对象1111 obj对象window对象
解析:
- o(),o是在全局执行的,而f1是箭头函数,它是没有绑定this的,它的this指向其父级的this,其父级say办法的this指向的是全局作用域,所以会打印出window;
- obj.say(),谁调用say,say 的this就指向谁,所以此时this指向的是obj对象;
- obj.pro.getPro(),咱们晓得,箭头函数时不绑定this的,getPro处于pro中,而对象不形成独自的作用域,所以箭头的函数的this就指向了全局作用域window。
首屏和白屏工夫如何计算
首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是onPageFinished事件。
白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。
办法1:当页面的元素数小于x时,则认为页面白屏。比方“没有任何内容”,能够获取页面的DOM节点数,判断DOM节点数少于某个阈值X,则认为白屏。 办法2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。 办法3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。
代码输入后果
var myObject = { foo: "bar", func: function() { var self = this; console.log(this.foo); console.log(self.foo); (function() { console.log(this.foo); console.log(self.foo); }()); }};myObject.func();
输入后果:bar bar undefined bar
解析:
- 首先func是由myObject调用的,this指向myObject。又因为var self = this;所以self指向myObject。
- 这个立刻执行匿名函数表达式是由window调用的,this指向window 。立刻执行匿名函数的作用域处于myObject.func的作用域中,在这个作用域找不到self变量,沿着作用域链向上查找self变量,找到了指向 myObject对象的self。