1.JS的数据类型
基本数据类型 String Number Boolean Symbol undefined null -- 指的是简单的数据段,按值访问
引用数据类型 Object(Array Date Regxp Function) -- 可能有多个值构成的对象 按引用访问
栈内存保存基本数据类型,是大小固定的数据。引用数据类型保存在堆内存中,在栈中保存了指针。
typeof 返回值 number, boolean, string, undefined, object, function,symbol(ES6以上版本才有)
NaN(not a number)是代表非数字值的特殊值,表示某个值不是数字但是参与计算,结果就是 NaN,用 isNaN 识别
Infinity 正无穷和负无穷 Number.isFinite
null 空引用
Symbol 类似于字符串的数据类型,独一无二的值,通过Symbol()函数生成,不能使用new命令
typeof 返回值
2.函数节流、函数防抖
链接1
链接2
函数防抖:一个事件被多次触发 只执行最后触发的一次。每次触发事件时设置一个延迟调用方法,取消之前的延时调用方法;1、搜索框输入
函数节流:一个事件被触发多次 在一定时间段内只调用一次事件处理函数 DOM元素的拖拽功能实现(mousemove)2、滚动事件scroll。
11、封装继承多态
封装:将属性和方法封装起来,然后通过一个外部可以调用的特定接口进行调用,构造函数+new创建实例
继承:当多个封装函数存在相同的属性和方法时,把这些相同的属性和方法提取到一个公共的方法中,通过组合继承将原型链和借用父构造函数组合到一块,用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承;Person.call(this, name, age);//构造函数继承;Student.prototype = new Person();//原型链继承 Object.defineProperty(Student.prototype,'constructor',{});//修复构造函数指向
ES6 使用 extents+super()方法
多态:在执行同一操作且作用于不同对象时,返回不同的结果 。把做什么和由谁去做分开,让代码更容易阅读维护
3.浅拷贝与深拷贝
浅拷贝:只复制对象的指针,新对象和源对象还是共享同一块内存,(浅拷贝也称引用拷贝,相当于取别名,)Object.assign、concat、ES6 展开运算符... 属于浅拷贝,但是数组第一层数据是基本数据类型 concat 返回的新数组属于深拷贝,对象第一层数据是基本数据类型而且 Object.assign 第一个参数为空对象返回的新对象也属于深拷贝。
深拷贝:会另外创造一个一模一样的对象,新对象跟源对象不共享内存,互不干扰。深拷贝也被称为值拷贝。
Object.assign(target, ...sources) 将一个或多个源对象所有可枚举属性的值复制到目标对象。它返回目标对象,第一级属性深拷贝,从第二级属性开始就是浅拷贝。
$.extend([deep], target, object1, [objectN]) 第一个参数是true就是深拷贝 不添加就是浅拷贝
JSON.parse(JSON.stringify())
拷贝后的缺陷:
function => 消失
undefined ===》 消失
Infinity ===》 null
NaN ===》 null
错误信息 ===》 空对象
//递归
var obj = {
id:1,
name:'Andy',
msg: {age:18},
color:['pink','red']
}
var newObj = {}
//封装函数
function deepCopy(newObj,obj) {
for(var key in obj) {
//判断属性值属于哪种数据类型
//属性值 obj[key]
//1.判断这个值是否为数组(数组也属于特殊对象,也是引用类型数据)
if(obj[key] instanceof Array) {
newObj[key] = []
deepCopy(newObj[key],obj[key]) //运用递归,把原对象属性值给新对象
//判断这个值是否为对象
} else if(obj[key] instanceof Object) {
newObj[key] = {}
deepCopy(newObj[key],obj[key]) //运用递归,把原对象属性值给新对象
} else {
//若是基本数据类型
newObj[key] = obj[key]
}
}
}
deepCopy(newObj,obj);
7、什么是伪数组?
1、有 length 属性,但length属性不是动态的,不会随着成员变化而变化
2、按索引方式存储数据。
3、没有数组方法,比如push()、pop()等方法。Array.isArray()返回false
比如函数内部的 arguments 对象,调用 document.getElementsByClassName document.queruySelectAll 之类的返回 NodeList对象都属于伪数组。
jQuery中的 $() 对象都是伪数组对象,保存的是DOM对象。
arguments可以通过Array.prototype.slice.call(fakeArray) 将伪数组转换成真正的Array对象;
10、什么是Ajax和JSON,它们的优缺点?
Ajax Asynchronous Javascript And XML/异步的javascript和xml
优点:
在页面不重载全部内容的情况下加载局部内容,降低数据传输量
避免用户不断刷新或者跳转页面,提高用户体验
缺点:
实现ajax下的前后退功能比较复杂,(不知道location.hash合不合适-更新URL 不重载页面)
跨域问题限制-开发中由后端允许跨域
会增加请求次数
对搜索引擎不太友好
JSON:
1、一种轻量级的数据交换格式,占用带宽小;
2、容易阅读编写解析;
3、支持复合数据类型;
4、基于纯文本,跨平台传递极其简单,Javascript原生支持;
缺点:相对xml通用性较差,数据可描述性较差
11、JSONP与JSON
JSONP是一种非官方跨域数据交互协议
JSONP是怎么产生的
1、Ajax直接请求普通文件有跨域无权限访问的问题,跨域是发送请求的url的 协议、域名、端口号三个任意一个与当前页面地址不同
2、利用<script>标签src属性没有跨域限制,创建一个<script>元素,src指向请求API网址,并提供一个回调函数来接收数据;(Web页面拥有"src"属性的标签都拥有跨域的能力)
dataType: "jsonp",
jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback:"flightHandler",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数
12、js中的同步和异步
JS 是一门“单线程”的语言,比如一条流水线,不能同时进行多个任务和流程,差别就在流水线上各个流程的执行顺序不同。
同步任务指的是,在主线程上排队执行的任务,形成一个执行栈(execution context stack),只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"通知主线程请求执行任务,该任务才会进入主线程执行。
最基础常见的异步是setTimeout和setInterval函数、Ajax。
异步转同步的方式: 回调函数、promise、async/await
13、get 与 post
1、get参数通过url传递,参数长度限制在2kb(2048字符),会完整保存在历史记录里,可以在浏览器书签收藏
2、post 放在request body 中,默认不限制长度,理论上最多80kb,不会保存在历史记录里,所以不能收藏
9、get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
3、GET请求在浏览器刷新或者回退的时候是无害的,POST的话会表单重复提交
4、get产生一个TCP数据包,浏览器会把http header和data一起发送出去,服务器响应200(返回数据);post产生两个TCP数据包,浏览器第一次先将header发送过去,服务器若响应100 continue,确认服务器和网络没问题可以服务,浏览器再发送data,服务器响应200 ok。【网络环境差时需要时间略长,在验证数据包完整性上,有很大优点】
7、GET数据类型只能urlencode编码,POST支持多种(json,form-data,x-www-form-urlencoded)
8、GET只接受ASCII字符编码格式,而POST没有限制(UTF-8)
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法
15、原生js的window.onload与jQuery的$(document).ready(function(){})有什么不同?如何用原生js实现ready方法?
window.onload方法必须等到页面内包括图片的所有元素加载完毕后才能执行。
$(document).ready()是DOM结构绘制完毕后就执行,对应原生JS的DOMContentLoaded方法
function ready(fn){
var d = document; //提高性能
if(d.addEventListener){
d.addEventListener("DOMContentLoaded",function(){
//注销事件,避免反复触发
d.removeEventListener("DOMContentLoaded",arguments.callee,false);
fn();
},false)
} else if(d.attachEvent){ //ie9以下
d.attachEvent("onreadystatechange",function(){
d.detachEvent("onreadystatechange",arguments.callee);
fn();
})
}
}
window.onload = function () {
alert('onload');
};
ready(function () {
alert('ready');
});
22、“strict mode”的作用 ('use strict')
是一种更严格的代码检查机制,让代码更加安全;提高编译器效率,增加运行速度;为新版本Javascript做好准备
不安全不合理不规范的代码可能导致SyntaxError异常
**比如**变量没有声明就赋值,严格模式会报错(默认是全局变量)
使用八进制语法
使用with语句(某些情况只允许静态绑定)
**严格模式下**对象不能有重名的属性,函数不能有重名的参数
禁止在if for非函数代码块内声明函数
禁止在函数内部遍历调用栈 不允许对arguments赋值 arguments不追踪参数的变化 禁止使用arguments.callee,(arguments-保存函数参数,这个对象有个叫callee的属性,属性是一个指针,指向当前函数,以前是用来递归的)(拥有 arguments 对象的函数)
delete禁止删除变量(以前删除变量不起作用),对象属性configurable=false时不可删除 Object.defineProperty-configurable
不允许使用关键字作为变量名或函数名 let const private, protected, public, static, interface, package,yield,implements
普通模式下在函数中调用this,this指向全局对象;严格模式函数中调用this指向undefined,当函数通过call和apply调用时,如果传入基础类型,在普通模式下this会自动指向其包装类(基础类型对应的Object类型,比如Boolean,Number等等);如果传入的是undefined和null,那么this指向的是global Object。严格模式下,this指向传入的值,不会做转换或变形。
24、内存泄露原因及解决办法
JS是一种垃圾收集式语言,内存是根据对象的创建分配(给该对象)的,会在没有这个对象的引用时由浏览器收回。IE和FireFox均使用引用计数来为 DOM 对象处理内存。如果计数为零,该对象就会被销毁。
原因
1. 使用未声明的变量,意外创建了一个全局变量
2. 变量循环引用,设置null
3. 忘记取消的计时器或回调函数,用完需要去 clearInterval/clearTimeout,(如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中)
4. 没有及时清理脱离 DOM元素 的引用,获取一个 DOM 元素的引用,后面这个元素被删除,但是保留了对这个元素的引用,所以它也无法被回收。dom元素引用=null
5. 监听事件的解除,监听的时候addEventListener,不监听的时候要使用removeEventListener
6. 没有及时清理闭包:不合理的使用闭包,导致某些变量一直被留在内存当中。
24、内存溢出原因
程序向系统申请内存时,系统不能满足程序要求,出现out of memory错误;比如一次从后端或本地缓存中取出太多数据,新建超大的数组
内存泄露堆积后导致out of memory错误
25、闭包
读取其他函数内部变量的函数 -- 定义在一个函数内部的函数。
1.可以读取函数内部变量
2.使局部变量始终保存在内存中,不会在调用结束后被JS垃圾回收机制回收;
注意点:
1.被引用的变量不能被销毁,增大了内存消耗,造成内存泄漏,退出函数前将不使用的局部变量赋值为null
2.闭包可能会在父函数外部改变父函数内部变量的值。所以如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。(?)
26、typeof 与instanceof 判断类型
typeof -- 获取一个变量或表达式的类型 instanceof -- 判断一个变量是否是某个对象的实例
基本类型 -- typeof => String Number Boolean undefined function Object
引用类型 -- instanceof =>
Array.isArray(arr);
constructor 属性返回对创建此对象的数组函数的引用
(a instanceof Array) //a是否Array的实例?true or false
(a.constructor == Array) // a实例所对应的构造函数是否为Array? true or false
8、var that=this;
this 关键字代表函数运行时自动生成的一个内部对象,在全局作用域使用时this指的是全局对象;在函数中this指的是全局对象,在构造函数中this指向这个构造函数创建的对象实例,在事件中,this指向接收事件的元素,严格模式下函数中this指向undefined
28、JS作用域、上下文。
作用域是在运行时一块代码中(代码某些特定部分)变量,函数和对象的可访问性。
在 ES5以前,采用全局和函数作用域,ES6 利用let const实现块级作用域,在if switch for while 循环语句,它们不会创建一个新的作用域。
上下文(context)指代码特定部分中 this 的值,取决于代码运行时环境,在全局作用域(scope)中上下文中始终是Window对象。
27、JS的apply与call的用法及意义
复用另外一个对象的方法,以另一个对象替换当前对象。
劫持别人的方法--实现继承
会改变函数运行时的上下文,改变函数体内部 this 的指向。
作用完全一样,只是接受参数的方式不太一样。
obj.call(thisObj, arg1, arg2, ...); //连续参数
obj.apply(thisObj, [arg1, arg2, ...]);//数组参数
把thisObj(即this)绑定到obj,这时候thisObj具备了obj的属性和方法。或者说thisObj『继承』了obj的属性和方法。
16、工厂模式、构造函数、原型模式
批量创建可复用对象,避免无意义的重复代码
工厂模式:
在函数内创建一个对象,给对象赋予属性和方法再将对象返回。
解决了创建多个相似对象的问题,但是工厂模式无法识别对象的类型。
全部都是Object,不像Date、Array等,因此出现了构造函数模式。
function createBlog(name, url) {
var o = new Object();
o.name = name;
o.url = url;
o.sayUrl= function() {
alert(this.url);
}
return o;
}
var blog1 = createBlog('luomg', 'https://segmentfault.com/');
构造函数模式
可以创建特定类型的对象,类似于Array、Date等原生JS的对象。问题在属性和方法无法得到复用,每次创建实例的时候都要重新创建一次方法
1.函数名首写字母为大写(惯例)
2.没有显示的创建对象
3.直接将属性和方法赋值给this对象
4.没有return语句
5.使用new创建对象
6.能够识别对象(这是构造函数模式的优点,比工厂模式更好的地方)
function Blog(name, url) {
this.name = name;
this.url = url;
this.alertUrl = function() {
alert(this.url);
}
}
var blog = new Blog('luomg', 'https://segmentfault.com/');
原型模式
创建的每个函数都有prototype (原型)属性,这个属性是一个指针,指向一个对象,这个对象包含特定类型的所有实例共享的属性和方法
使用原型对象的优点(也是缺点)就是可以让所有对象实例共享它包含的属性及方法。
function Blog() {
}
Blog.prototype.name = 'luomg';
Blog.prototype.url = 'https://segmentfault.com/';
混合模式(原型模式 + 构造函数模式)
function Blog(name, url, friend) {
this.name = name;
this.url = url;
this.friend = friend;
}
Blog.prototype.alertInfo = function() {
alert(this.name + this.url + this.friend);
}
var blog = new Blog('luomg', 'https://segmentfault.com/', ['fn1', 'fn2', 'fn3']);
动态原型模式
将所有信息封装在了构造函数中,通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),
这个可以通过判断该方法是否有效而选择是否需要初始化原型。
function Blog(name, url) {
this.name = name;
this.url = url;
if (typeof this.alertInfo != 'function') {
// 这段代码只执行了一次
alert('exe time');
Blog.prototype.alertInfo = function() {
alert(thia.name + this.url);
}
}
}
var blog = new Blog('luomg', 'https://segmentfault.com/');
也叫伪经典继承,将原型链和借用父构造函数组合到一块,用原型链实现对原型属性和方法的继承,借用构造函数来实现对实例属性的继承。这样在原型上实现了函数复用,又保证每个实例都有它自己的属性。
function Person(name, age) {
this.name = name;
this.age = age;
this.setAge = function () {console.log("Person");}
}
Person.prototype.setAge = function () {console.log("Person--"+this.age);}
function Student(name, age, price) {
Person.call(this, name, age);//构造函数继承
this.price = price;
this.setAge = function () {console.log("Student");}
this.setScore = function () {console.log("setScore--"+this.price);};
}
Student.prototype = new Person();
Object.defineProperty(Student.prototype,'constructor',{
enumerable:false,
value:'Student',
configurable:false
});//组合继承也是需要修复构造函数指向的
Student.prototype.setAge = function () {console.log("Student--"+this.age);};
Student.prototype.sayHello = function () { };
var s1 = new Student('Tom', 20, 342);//s1.constructor
var s2 = new Student('Jack', 22, 56744);//s1.__proto__===Student.prototype
var p1 = new Person('person', 13232);
17、new 操作符具体干了什么?
1、首先创建了一个新对象
2、设置原型,将对象的原型设置为函数的prototype对象
3、让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
4、判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
18、原型链
指的是构造函数、原型和实例的关系。构造函数都有一个prototype原型对象,原型对象和实例都包含一个指向构造函数的指针constructor,实例都包含一个指向原型对象的隐式原型(内部指针)__proto__。
当访问对象的某个属性时,会先在这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,也就是父类构造函数的prototype,这样一层一层向上查找就形成一个链式结构,称为原型链。
19、构造函数相关的检测方法
访问实例属性 obj.hasOwnProperty(key);
访问原型属性 !obj.hasOwnProperty(key) && key in obj
返回一个可枚举属性的字符串数组 Object.keys(obj || Object.prototype);
得到所有实例属性 Object.getOwnPropertyNames(obj || Object.prototype)
确定原型与实例的关系 obj instanceof Object; Object.prototype.isPrototypeOf(obj);
21、浏览器如何渲染解析页面?
渲染引擎通过网络获得所请求文档的内容
解析html构建dom树 -> 将CSS解析成CSS 规则树 -> 结合dom树和CSS规则树构建渲染树 -> 布局渲染树 -> 绘制render树。
其中某部分发生变化影响布局,就会 回流(Reflow -- 重新渲染) 重绘(Repaint -- 重画某部分),影响性能。所以写代码时可以先定义好CSS再去修改DOM的className,不要一条一条修改DOM的样式。
23、 JS 跳转
刷新 window.location.reload()
前进 window.history.go(1) window.history.href=url
后退 window.history.go(-1) -- 表单中的内容会丢失
后退 window.history.back() -- 表单中的内容会保留
history.back()的刷新版本 window.location.replace(document.referrer)
[前进 window.history.forward()]
29、eval();
eval()函数就像是一个ECMAScript解析器,只接收一个参数,即执行的ECMAScript字符串。将传入的字符串当作实际的语句来解析。
20、Javascript获取页面元素的位置
②获取网页的大小(只读属性)
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
③获取网页元素的绝对位置--offsetTop和offsetLeft属性
function getElementLeft(element){
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null){
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
29、http与https
*HTTP* 是超文本传输协议,是客户端和服务器端请求和应答的标准(TCP);
以明文方式发送内容,传输的数据都是未加密的。如果攻击者截取Web浏览器和网站服务器之间的传输报文,就可以直接读取其中的信息,所以不适合传输一些敏感信息,比如:信用卡号,密码支付信息。
默认80端口。
*https*
安全套接字层超文本传输协议HTTPS,默认443端口。为了安全传输数据,HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,为浏览器和服务器之间的通信加密。
建立一个信息安全通道保证数据传输的安全;确认网站真实性。
1. https 缓存不如 http 高效,会增加数据开销
2. HTTP连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输,身份认证的网络协议,比HTTP协议安全
3. HTTPS协议需要到ca申请证书,免费证书较少,功能越强大的证书费用越高
JS如何查找元素? document.
getElementById()
getElementsByClassName()
getElementsByName()
getElementsByTagName()
querySelector()
querySelectorAll()
节点指针
父节点.firstChild -- firstElementChild
父节点.lastChild -- lastElementChild
父节点.childNodes
兄弟节点.previousSibling -- previousElementSibling
兄弟节点.nextSibling -- nextElementSibling
子节点.parentNode
JS如何创建节点? docuement.
createElement(元素标签) 创建元素节点
createAttribute(元素属性)
createTextNode(文本内容) 创建文本节点
JS如何操作节点(插入、替换、复制、删除)?
appendChild(添加的新子节点) 向子节点列表末尾添加新的子节点
insertBefore(插入当前节点的新节点,已知子节点) 在已知的子节点之前插入新的子节点
son.parentNode.insertBefore
替换节点 replaceChild(要插入的新元素, 将被替换的老元素)
删除节点 removeChild(要删除的节点)
复制节点.cloneNode(true/false) true -- 复制当前节点及其所有子节点 false -- 仅复制当前节点
JS属性操作?
获取元素属性.getAttribute(元素属性名)
设置属性.setAttribute(元素属性名, 属性值)
删除属性removeAttribute(元素属性名)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。