Javascript是一种弱类型语言,它分别有什么优点和缺点
弱类型语言:简单好用,更灵活多变。但是会牺牲性能,比如一些隐含的类型转换
强类型语言:类型转换的时候非常严格,,强类型语言是直接操纵内存,容易出内存越界和泄漏的问题。在类型的转换方面是很多约束,甚至强制转换也要很谨慎,一不小心就出大问题。
Javascript里面的数据类型有哪些
5个简单数据类型(基本数据类型)+ 1个复杂数据类型
undefiend, number string null boolean + object
有几种方式可以判断数据类型
typeof和intanceof
其实typeof和instanceof的目的都是检测变量的类型,两个的区别在于typeof一般是检测的是基本数据类型,instanceof主要检测的是引用类型!
Alt text
基本类型和引用类型有什么区别
赋值的时候基本类型按值,引用类型按引用,就是基本类型会复制一份,引用类型就是一个新的指针
函数传参的时候都是按值传递
{}{}? [][]? nullundefined?
{}{}()
[]==[]()
null == undefined(对)
写个方法判断一个变量的类型
函数
函数声明和函数表达式有什么区别
函数声明会将那个函数提升到最前面,成为全局函数。函数声明要指定函数名,而函数表达式不用,可以用作匿名函数。
创建函数的方式:函数声明、函数表达式、还有一种不常见的方式就是Function构造器。
函数声明:
function add(a,b) {
a = +a;
b = +b;
if (isNaN(a) || isNaN(b)) {
return;
}
return a + b;
}
函数表达式的几种方式:
// 函数表达式
var add = function(a, b) {
// do sth
}
// 匿名函数定义的一个立即执行函数表达式
(function() {
// do sth
})();
// 作为返回值的函数表达式
return function() {
// do sth
};
// 命名式函数表达式
var add = function foo (a, b) {
// do sth
}
函数声明与函数表达式的主要区别就是:函数声明会被前置
函数声明前置:
// function add(a,b) 已经声明前置了,可以正常调用
var num = add(1, 2);
console.log(num); // 3
function add(a,b) {
a = +a;
b = +b;
if (isNaN(a) || isNaN(b)) {
return;
}
return a + b;
}
函数表达式前置:
// var add 变量声明提前,此时变量的值是undefined
var num = add(1, 2);
console.log(num); // TypeError:undefined is not a function
var add = function(a, b) {
a = +a;
b = +b;
if (isNaN(a) || isNaN(b)) {
return;
}
return a + b;
}
Function构造器:
var func = new Function(‘a’, ‘b’, ‘console.log(a+b);’);
fun(1, 2); // 3
// 和上面的方式没有区别
var func = Function(‘a’, ‘b’, ‘console.log(a+b);’);
func(1, 2); // 3
区别:
360截图20170307114742162.png
函数有哪几种调用方式
直接调用
作为对象的方法调用
apply,call
作用域
JS没有块作用域,只有函数作用域
作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。
作用域链是什么
A://说的不是很清楚
闭包
闭包是什么
闭包是指有权访问另一个函数作用域中的变量的函数
创建闭包的方式:
在一个函数内部创建另一个函数
闭包的作用:
1.让这些函数的值始终保存在内存中
滥用闭包有什么副作用
由于闭包会携带包含它的函数的作用域链,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,所以只在绝对必要时使用闭包。
闭包实现块级作用域
(function() {
})();
闭包的作用/应用
匿名自执行函数、缓存、实现封装(主要作用)、实现面向对象中的对象
1 匿名自执行函数
我们知道所有的变量,如果不加上var关键字,则默认的会添加到全局对象的属性上去,这样的临时变量加入全局对象有很多坏处,
比如:别的函数可能误用这些变量;造成全局对象过于庞大,影响访问速度(因为变量的取值是需要从原型链上遍历的)。
除了每次使用变量都是用var关键字外,我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,
比如UI的初始化,那么我们可以使用闭包:
var datamodel = {
table : [],
tree : {}
};
(function(dm){
for(var i = 0; i < dm.table.rows; i++){
var row = dm.table.rows[i];
for(var j = 0; j < row.cells; i++){
drawCell(i, j);
}
}
//build dm.tree
})(datamodel);
我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,
因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
**2缓存**
再来看一个例子,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,
然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,
从而函数内部的值可以得以保留。
var CachedSearchBox = (function(){
var cache = {},
count = [];
return {
attachSearchBox : function(dsid){
if(dsid in cache){//如果结果在缓存中
return cache[dsid];//直接返回缓存中的对象
}
var fsb = new uikit.webctrl.SearchBox(dsid);//新建
cache[dsid] = fsb;//更新缓存
if(count.length > 100){//保正缓存的大小<=100
delete cache[count.shift()];
}
return fsb;
},
clearSearchBox : function(dsid){
if(dsid in cache){
cache[dsid].clearSelection();
}
}
};
})();
CachedSearchBox.attachSearchBox("input1");
这样,当我们第二次调用CachedSearchBox.attachSerachBox(“input1”)的时候,
我们就可以从缓存中取道该对象,而不用再去创建一个新的searchbox对象。
**3 实现封装**
可以先来看一个关于封装的例子,在person之外的地方无法访问其内部的变量,而通过提供闭包的形式来访问:
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName());
得到结果如下:
undefined
default
abruzzi
4 **实现面向对象中的对象**
传统的对象语言都提供类的模板机制,
这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,
我们可以模拟出这样的机制。还是以上边的例子来讲:
function Person(){
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
};
var john = Person();
print(john.getName());
john.setName("john");
print(john.getName());
var jack = Person();
print(jack.getName());
jack.setName("jack");
print(jack.getName());
运行结果如下:
default
john
default
jack
由此代码可知,john和jack都可以称为是Person这个类的实例,因为这两个实例对name这个成员的访问是独立的,互不影响的。
####实现一个暴露内部变量,而且外部可以访问修改的函数
(get和set,闭包实现)
var person = function(){
//变量作用域为函数内部,外部无法访问
var name = "default";
return {
getName : function(){
return name;
},
setName : function(newName){
name = newName;
}
}
}();
print(person.name);//直接访问,结果为undefined
print(person.getName());
person.setName("abruzzi");
print(person.getName());
得到结果如下:
undefined
default
abruzzi
####从几个li中取下标的闭包代码
1
2
3
4
var li=document.getElementsByTagName("li");
for(var i=0;i<li.length;i++) {
(function(x) {
li[x].onclick=function(){alert(x);}
})(i);
}
####实现一个闭包的例子(实现了一个定时函数传值的)
闭包:
for(var i = 0; i < 10; i++ ){
(function(x){
setTimeout(function(){
console.log(x)
},x*1000)
})(i)
}
或者用全局变量实现
###面向对象
------------------------------------------
####原型与原型链
原型链是实现继承的主要方法,其基本原理是利用原型让一个引用继承另一个引用类型的属性和方法。
简单回顾一下构造函数,原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。我们让一个原型对象等于另一个类型的实例,此时原型对象将包含一个指向另一个原型的指针,另一个原型中也包含着指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。这就是所谓原型链的基本概念
####js原型链的继承
####静态属性怎么继承
####js原型链以及特点
因为每个对象和原型都有原型,对象的原型指向原型对象,
而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
####面向对象有哪几个特点
封装,继承,多态
####对象的继承
####创建对象的几种方式
####继承的两种方法
原型链继承和类继承。然后类继承只继承了实例属性,没有原型属性。
原型链继承可以继承所有。然后用apply和call怎么继承原型链上的共享属性?通过空函数传值。新建一个空函数C。C实例化后C的实例属性就是空,然后用B的apply/call去继承C,相当于继承了C的实例属性
####JS怎么实现继承
将父对象的一个实例赋值给子对象的原型
[js实现继承的5种方式](http://javapolo.iteye.com/blog/1996871)
####new操作符时具体是干了些什么
New:
1、创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。
3、新创建的对象由 this 所引用,并且最后隐式的返回 this 。
####怎么判断属性来自对象自身还是原型链
hasOwnProperty
####一个对象的实例,如何统计被几次调用过,分别具体被哪些函数调用过,可以有哪些方法
####写个Animal类
有个Cat类继承它,要求新建一个Cat的实例,可以调用catch方法输出自己的名字“大白的猫”;
####实现私有变量
(这里当时还没看到相关部分,只能想到用属性的setter、getter来做。。。?面试官各种启发呀。。);
####手写一个类的继承,并解释一下
####手写JS实现类继承,讲原型链原理
####浅拷贝和深拷贝
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
var Doctor = extendCopy(Chinese);
Doctor.career = '医生';
alert(Doctor.nation); // 中国
// 深拷贝:
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
var Doctor = deepCopy(Chinese);
###数组
------------------------------------------
####Array对象自带的方法,一一列举
var arr = [0,1,2];
1.pop():删除数组的最后一个元素,减少数组的长度,返回删除的值。
这里是2.
2.push(3):将参数加载到数组的最后,返回新数组的长度。
现在arr中是:0,1,2,3
3.shift():删除数组的第一个元素,返回删除的值,同时数组长度减一。
这里是0
4.unshift(3,4):把参数加载数组的前面,返回新数组的长度。
现在arr:中是3,4,0,1,2
5.sort():按指定的参数对数组进行排序 ,返回的值是经过排序之后的数组
var a = [0,1,5,10,15];
var b = a.sort(); // a:[1,2,3,4,5] b:[0,1,10,15,5]
正确的排序:
function compare(val1, val2) {
return val1 - val2;
}
arr = [3,7,6];
arr.sort(compare); // 3,6,7
6.reverse():反转数组项的顺序,返回的值是经过排序之后的数组
7.concat(3,4):把两个数组拼接起来。 返回的值是一个副本
8.slice(start,end):返回从原数组中指定开始下标到结束下标之间的项组成的新数组
var colors = ["red", "green", "blue", "yellow", "purple"];
// 一个参数:[ ,+∞)
var color1 = colors.slice(1);
alert(color1); // green,blue,yellow,purple
// 两个参数:[,)
var color2 = colors.slice(1,4);
alert(color2); // green,blue,yellow
6splice():
删除: 2个参数,起始位置,删除的项数
插入: 3个参数,起始位置,删除的项数,插入的项
替换:任意参数,起始位置,删除的项数,插入的任意数量的项
var colors = ["red", "green", "blue"];
var removed = colors.splice(0,1); // 删除第一项
alert(colors); // green,blue
alert(removed); // red,返回数组中只包含一项
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1开始插入两项
alert(colors); // green,yellow,orange,blue
alert(removed); // 返回的数组是一个空数组
removed = colors.splice(1, 1, "red", "purple"); // 从位置I开始插入两项
alert(colors); // green,red,yellow,orange,blue
alert(removed); // yellow,返回的数组中只包含一项
indexOf()和lastIndexOf():接受两个参数,要查找的项(可选)和查找起点位置的索引
indexOf():从数组开头向后查找
lastIndexOf():从数组末尾开始向前查找
every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成数组。
forEach():对数组的每一项运行给定函数,这个方法没有返回值。
map():对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。
some():对数组的每一项运行给定参数,如果该函数对任一项返回true,则返回true。以上方法都不会修改数组中的包含的值。
reduce()和reduceRight():缩小数组的方法,这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。
join(separator):将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符
var a = [1,2,3,4,5];
var b = a.join("|"); //a:[1,2,3,4,5] b:"1|2|3|4|5"
####Array对象自带的排序函数底层是怎么实现的
####数组去重
思路:
1.创建一个新的数组存放结果
2.创建一个空对象
3.for循环时,每次取出一个元素与对象进行对比,如果这个元素不重复,则把它存放到结果数组中,同时把这个元素的内容作为对象的一个属性,并赋值为1,存入到第2步建立的对象中。
说明:至于如何对比,就是每次从原数组中取出一个元素,然后到对象中去访问这个属性,如果能访问到值,则说明重复。
Array.prototype.unique3 = function(){
var res = []; // 创建一个新的数组存放结果
var json = {}; // 创建一个空对象
for(var i = 0; i < this.length; i++){
if(!json[this[i]]){ // json不为空的时候
res.push(this[i]); // 把元素依次放入到res中
json[this[i]] = 1; //?
}
}
return res; // 返回res
}
var arr = [112,112,34,'你好',112,112,34,'你好','str','str1'];
alert(arr.unique3()); // 调用unique3函数
####数组去除一个函数。
用arr.splice。又问splice返回了什么?应该返回的是去除的元素。
####你知道伪数组吗
这里把符合以下条件的对象称为伪数组:
1,具有length属性
2,按索引方式存储数据
3,不具有数组的push,pop等方法
如
1,function内的**arguments** 。
2,通过**document.forms,Form.elements,Select.options,document.getElementsByName()** **,document.getElementsByTagName()** ,[**childNodes/children** ](http://snandy.iteye.com/blog/547369)等方式获取的集合(HTMLCollection,NodeList)等。
3,特殊写法的对象 ,如
var obj={};
obj[0] = "一";
obj[1] = "二";
obj[2] = "三";
obj.length = 3;
####那你重新设计一下这个函数,让它直接拥有数组的方法吧
// 方法一、 声明一个空数组,通过遍历伪数组把它们重新添加到新的数组中,大家都会,这不是面试官要的
var aLi = document.querySelectorAll('li');
var arr = [];
for (var i = 0; i < aLi.length; i++) {
arr[arr.length] = aLi[i]
}
// 方法二、使用数组的slice()方法 它返回的是数组,使用call或者apply指向伪数组
var arr = Array.prototype.slice.call(aLi);
####实现sum(2,3);sum(2,3,4);sum(2,3,4,5);
(我用了比较原始的方法,if来判断)
####那如果sum里面的参数不确定呢
(提取arguments的长度,用for循环)
####手写一个递归函数(考察arguments.callee,以及arguments的解释)
// arguments.callee 是一个指向正在执行函数的指针
function factorial(num) {
if (num <= 1) {
return 1;
}
else {
return num * arguments.callee(num - 1);
}
}
####若干个数字,怎么选出最大的五个
###AJAX
--------------------------------------------
#### ajax原理
Ajax的原理:通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。
####原生ajax的四个过程/手写原生ajax
a:new一个XHR对象
b:调用open方法
c:send一些数据
d:对过程进行监听,来知道服务器是不是正确地做出了响应,接着可以做一些事情
// 兼容性写法
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest(); //IE7+,Firefox,Chrome,Opera,Safari
}
else{
request = new ActiveXObject("Microsoft.XMLHTTP"); //IE6,IE5
}
// GET请求:
document.getElementById("search").onclick = function() {
var request = new XMLHttpRequest();
request.open("GET", "server.php?number=" + document.getElementById("keyword").value);
request.send();
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
document.getElementById("searchResult").innerHTML = request.responseText;
} else {
alert("发生错误:" + request.status);
}
}
}
}
// POST请求
document.getElementById("save").onclick = function() {
var request = new XMLHttpRequest();
request.open("POST", "server.php");
var data = "name=" + document.getElementById("staffName").value
+ "&number=" + document.getElementById("staffNumber").value
+ "&sex=" + document.getElementById("staffSex").value
+ "&job=" + document.getElementById("staffJob").value;
request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
document.getElementById("createResult").innerHTML = request.responseText;
} else {
alert("发生错误:" + request.status);
}
}
}
}
####通过哪个属性得到data?
jquery里是success回调里面的形参。
responseText和responseXML。后者是XML解析了的。
#### readyState几个状态的含义
readyState属性的变化代表服务器响应的变化
0:请求未初始化,open还没有调用
1:服务器连接已建立,open已经调用了
2:请求已接收,也就是接收到头信息了
3:请求处理中,也就是接收到了响应主体
4:请求已完成,且响应已就绪,也就是响应完成了
####支持哪些数据格式?
常用的有三种数据格式:HTML、XML、JSON。
####XML和JSON有过了解吧?能说一下分别介绍一下他们吗?JSON有什么优势
>**XML**:扩展标记语言 (ExtensibleMarkup Language, XML) ,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。
优点:
- A.格式统一,符合标准;
- B.容易与其他系统进行远程交互,数据共享比较方便。
缺点:
- A.XML文件庞大,文件格式复杂,传输占带宽;
- B.服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
- C.客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
- D.服务器端和客户端解析XML花费较多的资源和时间。
>**JSON**:(JavaScript ObjectNotation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。 JSON采用完全独立于语言的文本格式。
优点:
- A.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小;
- B.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读取;
- C.支持多种语言。包括ActionScript, C, C#, ColdFusion, Java, JavaScript, Perl, PHP, Python, Ruby等服务器端语言,便于服务器端的解析;
- D.在PHP世界,已经有PHP-JSON和JSON-PHP出现了,偏于PHP序列化后的程序直接调用,PHP服务器端的对象、数组等能直接生成JSON格式,便于客户端的访问提取;
- E.因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。
缺点:
- A.没有XML格式这么推广的深入人心和喜用广泛,没有XML那么通用性;
- B.JSON格式目前在Web Service中推广还属于初级阶段。
####在不支持json格式的浏览器中,写方法能够将json字符串与对象互相转换;
现代浏览器中提供了JSON.stringify()方法 将数组,对象转成json。
JSON.stringify 把一个对象转换成json字符串,
JSON.parse 把一个json字符串解析成对象。
####ajax在jquery的底层是怎么实现的
####手写一个promise版的ajax
####js的异步加载,promise的三种状态,ES7中的async用过么
####如果页面初始载入的时候把ajax请求返回的数据存在localStorage里面,然后每次调用的时候去localStorage里面取数,是否可行。
(直接说了不能保证数据的实时性,请求和实时性必然会有一方有所牺牲)
####js异步的方法
方案一:`<script>`标签的`async="async"`属性(详细参见:script标签的async属性)
点评:HTML5中新增的属性,Chrome、FF、IE9&IE9+均支持(IE6~8不支持)。此外,这种方法不能保证脚本按顺序执行。
方案二:`<script>`标签的`defer="defer"`属性
点评:兼容所有浏览器。此外,这种方法可以确保所有设置defer属性的脚本按顺序执行。
方案三:动态创建`<script>`标签