1、使用 typeof bar === "object" 判断 bar 是不是一个对象有神马潜在的弊端?如何避免这种弊端?
使用 typeof 的弊端是显而易见的(这种弊端同使用 instanceof):
let obj = {};
let arr = [];
console.log(typeof obj === 'object'); //true
console.log(typeof arr === 'object'); //true
console.log(typeof null === 'object'); //true
从输出结果可以知道,typeof bar==='object'不能准确的判断bar就是一个object.可以通过 Object.prototype.toString.call(bar) === "[object Object]"来避免这种弊端:
console.log(Object.prototype.toString.call(obj)); //[object Object]
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(null)); //[object Null]
Object.prototype.toString相关信息:http://www.jb51.net/article/79941.htm 2、(function(){
var a = b = 3;
})();
console.log(b)//3;
console.log(a)//a is not defined;
等同于:
(function(){
b=3;
var a=b;
})();
3、 知识点:www.jb51.net/article/771…
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;
注意:被谁调用,不是处于谁的作用域,即使在作用域
1、func是由myObject调用的,this指向 myObject。
2、self指向myObject,相当于 myObject的this的副本。
3、这个立即执行匿名函数表达式(IIFE)是由window调用的,this指向 window 。
4、IIFE的作用域处于myObject.func的作用域中,本作用域找不到self变量,沿着作用域链向上查找self变量,找到了指向 myObject对象的 self。
例二:
function outer(){
this.i = 10;
console.log(this.i);
this.text1=(function(){
console.log(this.i);
}());
this.text2=function(){
console.log(this.i);
};
};
outer();//10 10
console.log('**')
var ex=new outer();
ex.i='test';
ex.text2();//test
ex.text1();//Uncaught TypeError: ex.text1 is not a function(…)
this是据以执行的对象,就是谁调用的,例二是在window中调用,this自然指向window,
4、for(var i = 0; i < 5; i++) {
console.log('test');
setTimeout(function() {
console.log(i);
}, 0);
}
//每一个setTimeout在执行时,会返回一个唯一ID,
输出内容是:5 5 5 5 5
setTimeout是异步,js的事件运行机制是先执行同步的,再执行异步,此时的i=5,使用var定义的变量i的作用域是函数,那么console i的值为for 循环的i值。
我们就必须借助闭包的特性,每次循环时,将i值保存在一个闭包中,当setTimeout中定义的操作执行时,则访问对应闭包保存的i值即可。
for(var i = 0; i < 5; i++) {
console.log('test');
(function(){
setTimeout(function() {
console.log(i);
}, 0);
})(i);
}
//www.jianshu.com/p/cd3fee40e…
6、下面两个函数的返回值是一样的吗?为什么?
function foo1()
{
return {
bar: "hello"
};
}
function foo2()
{
return
{
bar: "hello"
};
}
foo1输出:object {bar:'hello'};
foo2: undefined;
在编程语言中,基本都是使用分号(;)将语句分隔开,这可以增加代码的可读性和整洁性。而在JS中,如若语句各占独立一行,通常可以省略语句间的分号(;),JS 解析器会根据能否正常编译来决定是否自动填充分号:
var test = 1 +
2
console.log(test); //3
等同于:var test=1+2;
console.log(test)//3
在上述情况下,为了正确解析代码,就不会自动填充分号了,但是对于 return 、break、continue 等语句,如果后面紧跟换行,解析器一定会自动在后面填充分号(;);
7、console.log(1+ +"2"); 等同于:console.log(1+(+'2'));
+'2' 的 + 是一元操作符,对 '2' 进行Number()操作,转为数字的2,所以等于3;
除了加法运算符有可能把运算子转为字符串,其他运算符都会把两侧的运算子自动转成数值。
自动转换类型:javascript.ruanyifeng.com/grammar/con…
8、
console.log(0.1 + 0.2);//0.30000000000000004
console.log(0.1 + 0.2 == 0.3);//false
计算机将所有数据以二进制的形式存储的,
把0.1和0.2转换成二进制:
0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
但是计算机只能用有限的位数来存一个数,所以最终,计算机存的数是一个近似于 0.1 的小数。
所以当我们计算 0.1 + 0.2 时,实际上算的是两个近似值相加,得到的值当然也是近似等于 0.3。
比如1/2,1/8,1/1024,这种2的倍数的可以精确的转换。
9、实现函数 isInteger(x) 来判断 x 是否是整数
可以将 x 转换成10进制,判断和本身是不是相等即可:
function isInteger(x) {
return parseInt(x, 10) === x;
}
ES6 对数值进行了扩展,提供了静态方法 isInteger() 来判断参数是否是整数:
10、(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
输出内容是:1,4,3,2
JavaScript是单线程,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。js中所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行。
js 事件运行机制:www.ruanyifeng.com/blog/2014/1…
11、写一个少于80 字符的函数,判断一个字符串是不是回文字符串
function isPalindrome(str) {
return (str == str.split('').reverse().join(''));
}
//split() 方法用于把一个字符串分割成字符串数组。
//reverse() 方法用于颠倒数组中元素的顺序。
//join() 方法用于把数组中的所有元素放入一个字符串。元素是通过指定的分隔符进行分隔的。
12、
for (var i = 0; i < 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
(function(i){
btn.addEventListener('click', function(){ console.log(i); });
})(i); document.body.appendChild(btn);
}
13、下面的代码会输出什么?为什么?
console.log(1 + "2" + "2");//122
console.log(1 + +"2" + "2");//32
console.log(1 + -"1" + "2");//02
console.log(+"1" + "1" + "2");//112
console.log( "A" - "B" + "2");//NaN2
console.log( "A" - "B" + 2);//NaN
14、下面的代码会输出什么?为什么?
var arr1 = "john".split(''); //j o h n
var arr2 = arr1.reverse(); //n h o j
var arr3 = "jones".split(''); //j o n e s
var arr4 = arr2.push(arr3);
console.log(arr1);//["n", "h", "o", "j", Array[5]]
console.log(arr2);//["n", "h", "o", "j", Array[5]]
console.log(arr4);//5
这个完全做错了。
第一个错误:直接把arr3的数组信息逐个加在了arr2后,[n,h,o,j,j,o,n,e,s];
第二个错误,认为arr1的值是[j,o,h,n];
第三个错误,arr4应该等于arr2 push 后的值;
第一个错误就不说了,弱智错误,
第二个错误,
JS中没有指针,只有传值(value)与传址(reference引用)的区别,数组对数组是引用类型,数组对变量是值类型
var a = [1,2,3,4] //a不仅是数组,还是个对象,实际上a就是对[1,2,3,4]的引用
var b=a
var c=a
//以上两条赋值语句建立了b与c 对a即[1,2,3,4]的引用,无论改变a 还是b抑或c 都是对[1,2,3,4]的操作,这就是传址(堆中操作)
var d=a[1] //则是把a[1]的值"1"传递给d,对d的改变则不会影响a[1],即所谓的传值(栈中操作)
第三个错误,
unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
pop() 方法用于删除并返回数组的最后一个元素。
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
参考:www.cnblogs.com/bydclouds/p…
15、var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456; console.log(a[b]); //456
a的属性名需要字符串,但是b和c是对象。因此,key值转化字符串,调用了toString()方法,所以,实际的效果:
a["[object Object]"] = 123;
a["[object Object]"] = 456;
16 原型,原型链,作用域,作用域链
原型:函数都有一个原型属性prototype,注:这个prototype的属性值是一个对象,默认的有一个constructor的属性,指向这个函数本身。对象有一个隐藏属性proto,该实例对象的proto属性指向原型对象prototype.
原型链:查找一个对象属性时,先查找基本属性,若没有,则沿着proto这条链向上找,这就是原型链。
作用域(scope):已声明变量或者函数的生命周期和作用范围。
作用域链:当查找一个变量时,先从当前作用域寻找,若找不到,就会去定义当前作用域的函数作用域范围内寻找,若找不到会一直查找到全局作用域,全局作用域都没有找到,就真的没有啦。
上下文执行环境:
17、什么是闭包?举例说明
有两种应用情况:
一是:函数作为返回值,
function fn(){
var max=10;
return function(x){
if(x>max){
console.log(x);
}
}
}
var f1=fn();
f1(15);
二是:函数作为参数被传递:
var max=10;
fn=function(x){
if(x>max){
console.log(x);
}
};
(function(f){
var max=100;
f(15);
})(fn);
fn函数作为一个参数被传递到另一个函数,复制给f参数。执行f(15)时,max 变量的取值是10,而不是100.
18、类
19、Null 和Undefined的区别
Undefined是代表声明未赋值的变量,变量的默认值是undefined;
Null是不存在的对象。
typeOf undefined=Undefined typeOf null=Object;
20、apply,call的区别???
apply,call的作用都是:继承另外一个对象的属性.
21、优化下面代码:
var str="我喜欢我可爱的女朋友,";
str=str+"她叫喵喵,";
str=str+"她时而可爱,时而认真,";
str=str+"她那天真的笑声可以让人忘掉一切烦恼。";
console.log(str);
优化后的:
var arr=[];
arr.push(‘我喜欢我可爱的女朋友,’);
arr.push(‘她叫喵喵,’);
arr.push(‘她时而可爱,时而认真,’);
arr.push(‘她那天真的笑声可以让人忘掉一切烦恼。’);
console.log(arr.join(‘’));
22、get和post 请求的区别。
1、get请求的数据会附在URL后,post 请求是放在HTTP header中一起传送到URL请求,用户是看不到的;
2、Get请求大小有限制,Post 的数据量比较大,默认没有限制。
3、get 请求和post 表达的语义不一样,get是用于信息获取,而且应该是安全的和幂等的,当然这里的安全指的是用于获取数据而非是修改数据,因此除了返回结果不会产生其他的副作用,而且绝大部分的get请求都直接被CDN缓存了,大大减少服务器的负担。post是读取修改信息,非幂等的,会产生副作用,所以必须交由web服务器处理。
4、get请求的URL可以被手动修改,请求的URL是可以被搜索引擎收录的,请求的URL可以存在书签里,或者历史里,或者分享给别人。
23、为什么学习NodeJS ?koa,hapi比较?
1、node的是基于google浏览器的v8引擎,保证了NOdeJS的性能和稳定性。2、NodeJS 的开发非常的高效,而且代码简单,而且NodeJS是异步编程,让Nodejs处理IO密集型应用有了明显的优势。3、NodeJS的社区很壮大,不仅包的数量在快速增加,包的质量也明显好于其他语言。
不适合的领域:1、计算密集型的应用 2、大内存的应用,由于V8引擎有内存设计的限制。32位环境中最大是1G,64位的环境最大是2G,如果一次读入10G数据对于NodeJS来说也是无法实现的。3、静态服务器,虽然Node的优势在于IO密集处理,但是和nginx的处理静态资源还是有很大的差距的。
24、关于前后端分离的理解。
25、浏览器的生成页面操作???
1、加载HTML:用户输入域名,浏览器根据域名进行URL解析,向服务器发送请求,服务器返回HTML文件。
2、解析HTML:从HTML解析出DOM tree,CSS 规则树,
3、渲染:根据Dom树和css 规则树构建一个渲染树,rendering tree,
4、绘制:遍历DOM 树,绘制节点。
回流:当操作DOM,某个部分影响布局,就需要去重新渲染页面。
重绘:改变DOM的背景颜色,字体颜色等,不影响元素周围或内部的属性,将引起浏览器的重绘,
前端渲染的优点:
1、减少服务器的资源
2、富交互
3、局部刷新
4、懒加载
5、一次编码多端执行
缺点呢:
1、不利于SEO
2、增加请求次数,渲染页面慢
方法:
1、渲染页面加一下loading效果。
2、部分同构;指前后端共用 JS,首次渲染时使用 Node.js 来输出 HTML
25、虚拟DOM
虚拟DOM 的核心思想是最小化操作DOM,使执行效率得到保证。
DOM很慢,而javascript对象处理很快。这样我们用javascript对象表示DOM信息和结构,当状态变更的时候,重新渲染javascript的对象结构。
用新的树和旧的树做对比,记录两棵树的差异。记录下来的不同就是我们需要对页面真正DOM操作,然后把他们应用在DOM树上,页面就变更了。这样就可以做到:视图的结构确实是整个全新渲染了,但是最后操作DOM的时候至少变更了不同的地方
CSS 部分
1、CSS样式的优先级
(1)相同权值情况下,CSS样式的优先级就是就近原则。!important>内联样式>内嵌样式>外链样式
(2)全职不同是,根据权值来判断,标签的权值是1,类的权值是10,ID的权值是100;
(3)若使用 js 修改,行间样式box.style.background = "red"; > box.className = ("blue");
2、css display属性有哪些值,各有什么表现;
none:元素隐藏,而且元素显示的空间也不会保留,属性visibility:hidden是保留元素的空间的;
block:将元素设置为块级元素,元素单独占一行,可以设置width和height了。
inline:将元素设置为行内元素,元素前后没有换行符。而且无法设置宽高
inline-block:行内块元素。这个属性值融合了inline 和 block 的特性,即是它既是内联元素,又可以设置width和height。
inherit: 规定应该从父元素继承 display 属性的值。
table: 此元素会作为块级表格来显示(类似
HTML 部分
1、网站性能优化
(1)减少一个页面访问所产生的http连接次数;
尽量合并js和css文件,减少独立文件个数;可以使用雪碧图 减少图片的数量
(2)将CSS放在页面顶端,JS文件放在页面底端
CSS的引用要放在html的头部header中,JS文件引用尽量放在页面底端标签的后面,主要的思路是让核心的页面内容尽早显示出来。不过要注意,一些大量使用js的页面,可能有一些js文件放在底端会引起一些难以预料的问题,根据实际情况适当运用即可;
(3)使JS,CSS文件内容最小化
使用一些javascript压缩工具对js脚本进行压缩,去除其中的空白字符、注释,最小化变量名等。
(4)尽量减少外部脚本的使用,减少DNS查询时间
不要在网页中引用太多的外部脚本,首先,一次dns的解析过程会消耗20-120毫秒的时间;其次,如果在页面中引用太多的外部文件(如各种广告、联盟等代码),可能会因为外部文件的响应速度而将你的网站拖得很慢。如果不得不用,那么就尽量将这些脚本放在页脚吧。不过有一点需要提及,就是浏览器一般只能并行处理同一域名下的两个请求,而对于不同子的域名则不受此限制,因此适当将本站静态内容(css,js)放在其他的子域名下(如 static.xxx.com)会有利于提高浏览器并行下载网页内容的能力。
(5)最大限度利用用户浏览器的cache来减少服务器的开销。
必须要了解浏览器访问url时的工作机制:
a. 第一次访问url时,用户从服务器端获取页面内容,并把相关的文件(images,css,js…)放在高速缓存中,也会把文件头中的expired time,last modified, Tags等相关信息也一同保留下来。
b. 用户重复访问url时,浏览器首先看高速缓存中是否有本站同名的文件,如果有,则检查文件的过期时间;如果尚未过期,则直接从缓存中读取文件,不再访问服务器。
c. 如果缓存中文件的过期时间不存在或已超出,则浏览器会访问服务器获取文件的头信息,检查last modifed和ETags等信息,如果发现本地缓存中的文件在上次访问后没被修改,则使用本地缓存中的文件;如果修改过,则从服务器上获取最新版本。
(6)动静分离。apache是一个功能完善但比较庞大的web server,它的资源占用基本上和同时运行的进程数呈正比,对服务器内存的消耗比较大,处理并行任务的效率也一般。在一些情况下,我们可以动静分离