一道关于原型链的面试题
function C1(name){
if(name) this.name=name;
}
function C2(name){
this.name=name;
}
function C3(name){
this.name=name||'john';
}
//
C1.prototype.name="Tom";
C2.prototype.name="Tom";
C3.prototype.name="Tom";
alert((new C1().name)+(new C2().name)+(new C3().name));
//我理解的本地name都没声明,访问的都是prototype的name属性
//所以有个疑问就是没有声明的属性也可以去原型里找吗
//不是应该声明没赋值才有用么
分析:
C1,if不成立,new C1()中没有name属性,就访问到了原型上的name,输出tom
C2,既然没有参数,也就是执行new C2(undefined),所以name为undefined
C3,new C3()的name值为john,所输出john
详解JS函数柯里化
https://www.jianshu.com/p/2975c25e4d71
一步步看清 async/await 和 promise 的执行顺序
https://juejin.im/post/5dc28ea66fb9a04a881d1ac0
https://segmentfault.com/a/1190000017224799
splice和slice、map和forEach、 filter()、reduce()的区别
1.slice(start,end):方法可以从已有数组中返回选定的元素,返回一个新数组,
包含从start到end(不包含该元素)的数组方法
注意:该方法不会更新原数组,而是返回一个子数组
2.splice():该方法想或者从数组中添加或删除项目,返回被删除的项目。(该方法会改变原数组)
splice(index, howmany,item1,...itemx)
·index参数:必须,整数规定添加或删除的位置,使用负数,从数组尾部规定位置
·howmany参数:必须,要删除的数量,
·item1..itemx:可选,向数组添加新项目
3.map():会返回一个全新的数组。使用于改变数据值的时候。会分配内存存储空间数组并返回,forEach()不会返回数据
4.forEach(): 不会返回任何有价值的东西,并且不打算改变数据,单纯的只是想用数据做一些事情,他允许callback更改原始数组的元素
5.reduce(): 方法接收一个函数作为累加器,数组中的每一个值(从左到右)开始缩减,最终计算一个值,不会改变原数组的值
6.filter(): 方法创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。它里面通过function去做处理
['1', '2', '3'].map(parseInt) 和 ['1', '2', '3'].fliter(parseInt)的输出结果是什么?
eg:
['1', '2', '3'].map(parseInt)
// [1, NaN, NaN]
其实在使用map时,map的callback的第二个参数index引索值就成为parseeInt的radix值。['1', '2', '3'].map(parseInt)在遍历的过程。其实是经历了下面的过程。
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);
parseInt('1', 0):radix的值为0,判断字符串发现介于1~9,用10进制转换,结果为1.
parseInt('2', 1):radix的值为1,如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。
parseInt('3', 2): radix的值为2,这就意味着字符串将被解析成字节数,也就是仅仅包含数值0和1。parseInt的规范指出,它仅尝试分析第一个字符的左侧。这个字符串的第一个字符是“3”,它并不是基础基数2的一个有效数字。所以这个子字符串将被解析为空。如果子字符串被解析成空了,函数将返回为NaN。
['1', '2', '3'].map(parseFloat)
// [1, 2, 3]
parseFloat相对于parseInt比较简单,不用考虑第二个参数,只需要看第一个参数是否能正常转换为数字就行。
parseFloat('1'); // 1
parseFloat('2'); // 2
parseFloat('3'); // 3
一个小的知识点:如何快速将一个字符串数组转化为数字类型的数组
['1', '2', '3'].map(parseFloat)
['1', '2', '3'].map(Number)
['1', '2', '3'].filter(parseInt)
// ["1"]
filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。
parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);
parseInt('1', 0):radix的值为0,判断字符串发现介于1~9,用10进制转换,结果为1,所以callback的结果等价于true,返回元素'1'。
parseInt('2', 1):radix的值为1,如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN,结果不等价于true,不返回。
parseInt('3', 2): radix的值为2,这就意味着字符串将被解析成字节数,也就是仅仅包含数值0和1。parseInt的规范指出,它仅尝试分析第一个字符的左侧。这个字符串的第一个字符是“3”,它并不是基础基数2的一个有效数字。所以这个子字符串将被解析为空。如果子字符串被解析成空了,函数将返回为NaN。
2、['1', '2', '3'].filter(parseFloat)
['1', '2', '3'].filter(parseFloat)
// ["1", "2", "3"]
使用parseFloat时,遍历之后结果的每一项都是结果等价于true,所以全部返回。
CSS3中transition和animation的属性分别有哪些
盒模型
forEach,map和filter的区别
实现函数柯里化
跨标签页的通讯方式有哪些
(1) BroadCast Channel
(2) Service Worker
(3) LocalStorage + window.onstorage监听
(4) Shared Worker + 定时器轮询(setInterval)
(5) IndexedDB + 定时器轮询(setInterval)
(6) cookie + 定时器轮询(setInterval)
(7) window.open + window.postMessage
(8) Websocket
javascript中函数声明与函数表达式的区别
1、以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用。
2、以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的。
3、在Javscript中,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非是一视同仁的,解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问),至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解析执行。
4、Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中
5、函数声明的优先级高于变量,如果变量名跟函数名相同且未赋值,则函数声明会覆盖变量声明;如果函数有多个同名参数,那么最后一个参数(即使没有定义)会覆盖前面的同名参数;
6、附:每个程序都要做的工作,程序开始先预解析语法,标点符号是否有误,解析内存是否可容纳,解析变量。。。直到解析无误了,程序才开始正常的流程顺序走。试想一下,如果没有预解析顺序,直接按照流程顺序走,额能程序执行到最后一个函数,发现了语法错误,才开始报错,那性能要有多差啊!
例子1:
fn()
var fn = function() {
console.log("这是函数表达式")
}
// fn is not a function
fn()
function fn() {
console.log("这是函数声明")
}
// 这是函数声明
例子2:
变量的查找是就近原则去寻找,定义的var变量;
第二点,变量的声明被提前到作用域顶部,赋值保留在原地,如下demo;
var a=10;
function aaa(){
alert(a);
var a=20;
}
aaa(); //结果为:undefined
例子3:
ar a=10;
function aaa(a){
alert(a);
var a=20; //因为 a 是形参,优先级高于 var a; 所以 局部变量a的声明其实被忽略了。
}
aaa(a); //结果为:10
例子4:
var a=10;
function aaa(){
alert(a);
};
function bbb(){
var a=20;
aaa();
}
bbb(); //结果为10,
因为aaa()函数不能访问到bbb()里面的局部变量,所以访问到的是a=10,这个全局变量。
此外,bbb()中的aaa()只是调用外部的函数,这一点要注意!如果放在里面,结果不一样
例子5:
var a=10;
function test(){
alert(a); // 函数执行后,在函数环境内,没有找到本层环境关于a的声明,所以开始向上一层环境查找。
a = 20; // 执行到这行开始改变全局a的量
};
test(); // 10
alert(a); // 20 。 全局环境的a在函数执行时已经被改变
出现上面的错误是因为用函数声明创建的函数可以在函数解析后调用(解析时进行等逻辑处理);而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。其本质原因体现在这两种类型在Javascript function hoisting(函数提升)和运行时机(解析时/运行时)上的差异。
如何实现浏览器内多个标签页之间的通信? (阿里)
调用localstorge、cookies等本地存储方式
如何判断当前脚本运行在浏览器还是node环境中?(阿里)
通过判断Global对象是否为window,如果不为window,当前脚本没有运行在浏览器中
精确判断对象的类型
JavaScript 中一切都是对象,任何都不例外,对所有值类型应用Object.prototype.toString.call() 方法结果如下
console.log(Object.prototype.toString.call(123)) //[object Number]
console.log(Object.prototype.toString.call('123')) //[object String]
console.log(Object.prototype.toString.call(undefined)) //[object Undefined]
console.log(Object.prototype.toString.call(true)) //[object Boolean]
console.log(Object.prototype.toString.call({})) //[object Object]
console.log(Object.prototype.toString.call([])) //[object Array]
console.log(Object.prototype.toString.call(function(){})) //[object Function]
window load 和document ready的区别
document ready 实质是DOMContentLoaded事件
所有的节点渲染完毕,就执行
load事件要等到所有的资源加载完毕,如图片,视频,js脚本等
实现对数组进行乱序
分析:这道题检验了数组的 sort() 方法,因为是乱序,所以还需要用到 Math.random() 产生随机数,打乱排序规律!
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var sign = 1;
a.sort(function() {
return Math.random() - 0.5
});
请写出异步加载js方案,不少于两种
(1) defer,只支持IE
(2) async:
(3) 创建script,插入到DOM中,加载完毕后callBack
defer延迟执行,并行加载js文件,会按照页面上script标签的顺序执行
async异步加载,并行加载js文件,下载完成立即执行,不会按照页面上script
标签的顺序执行
增进:
• JavaScript代码的加载和执行会阻塞下面HTML的渲染
• 二者都是异步去加载外部JS文件
• Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行;而
Defer是在JS加载完成后,整个文档解析完成后执行。
• Defer更像是将