JavaScript 有哪些数据类型
原始数据类型:
Boolean: 布尔表示一个逻辑实体,可以有两个值:true 和 false
Number: 用于表示数字类型
String: 用于表示文本数据
Null: Null 类型只有一个值: null,特指对象的值未设置
Undefined: 一个没有被赋值的变量会有个默认值 undefined
Symbol: 符号(Symbols)是ECMAScript第6版新定义的。符号类型是唯一的并且是不可修改的
引用类型:Object
介绍一下 JS 有哪些内置对象
Object 是 JavaScript 中所有对象的父对象
数据封装类对象:Object、Array、Boolean、Number、String
其他对象:Function、Argument、Math、Date、RegExp、Error
讲讲JS的语言特性
JavaScript脚本语言具有以下特点:
脚本语言
JavaScript是一种解释型的脚本语言,C、C++等语言先编译后执行,而JavaScript是在程序的运行过程中逐行进行解释。
基于对象
JavaScript是一种基于对象的脚本语言,它不仅可以创建对象,也能使用现有的对象。
简单
JavaScript语言中采用的是弱类型的变量类型,对使用的数据类型未做出严格的要求,是基于Java基本语句和控制的脚本语言,其设计简单紧凑。
动态性
JavaScript是一种采用事件驱动的脚本语言,它不需要经过Web服务器就可以对用户的输入做出响应。在访问一个网页时,鼠标在网页中进行鼠标点击或上下移、窗口移动等操作JavaScript都可直接对这些事件给出相应的响应。
跨平台性
JavaScript脚本语言不依赖于操作系统,仅需要浏览器的支持。因此一个JavaScript脚本在编写后可以带到任意机器上使用,前提上机器上的浏览器支 持JavaScript脚本语言,目前JavaScript已被大多数的浏览器所支持。不同于服务器端脚本语言,例如PHP与ASP,JavaScript主要被作为客户端脚本语言在用户的浏览器上运行,不需要服务器的支持。所以在早期程序员比较青睐于JavaScript以减少对服务器的负担,而与此同时也带来另一个问题:安全性。而随着服务器的强壮,虽然程序员更喜欢运行于服务端的脚本以保证安全,但JavaScript仍然以其跨平台、容易上手等优势大行其道。同时,有些特殊功能(如AJAX)必须依赖Javascript在客户端进行支持。随着引擎如V8和框架如Node.js的发展,及其事件驱动及异步IO等特性,JavaScript逐渐被用来编写服务器端程序。
JS数组有哪些常用方法?
修改器方法:
pop(): 删除数组的最后一个元素,并返回这个元素
push():在数组的末尾增加一个或多个元素,并返回数组的新长度
reverse(): 颠倒数组中元素的排列顺序
shift(): 删除数组的第一个元素,并返回这个元素
unshift(): 在数组的开头增加一个或多个元素,并返回数组的新长度
sort(): 对数组元素进行排序,并返回当前数组
splice(): 在任意的位置给数组添加或删除任意个元素
访问方法:
concat(): 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组
join(): 连接所有数组元素组成一个字符串
slice(): 抽取当前数组中的一段元素组合成一个新数组
indeOf(): 返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1
lastIndexOf(): 返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1
迭代方法:
forEach(): 为数组中的每个元素执行一次回调函数,最终返回 undefined
every(): 如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false
some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false
filter(): 将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回
map(): 返回一个由回调函数的返回值组成的新数组
JS字符串有哪些常用方法?
https://www.cnblogs.com/hcxy/p/7376648.html
截取字符串方法:
slice() 提取字符串的某个部分,并以新的字符串返回被提取的部分。
split() 用于把一个字符串分割成字符串数组。
substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符。
substring() 方法用于提取字符串中介于两个指定下标之间的字符。
其他方法:
charAt() 返回指定位置的字符。
charCodeAt() 返回在指定的位置的字符的 Unicode 编码。
concat() 用于连接两个或多个字符串。
indexOf() 方法 返回指定字符串在字符串中首次出现的位置。匹配不到则返回-1。
lastIndexOf() 返回指定字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
replace() 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
trim() 去除 str 开头和结尾处的空白字符,返回 str 的一个副本,不影响字符串本身的值
toLocaleUpperCase() / toLocaleLowerCase() 用于字符串转换大小写(与下面的方法方法仅在某些外国小语种有差别)
match() 在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。匹配不到返回Null。
search() 用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。无匹配返回-1。
fromCharCode() 接受一个指定的 Unicode 值,然后返回一个字符串。
substr()和 substring()有什么区别?
什么是命令式编程?什么是声明式编程?
https://www.jianshu.com/p/7316e3288fd0
- 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
- 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
例:将一个数组的每个值乘2;
命令式:
var numbers = [1,2,3,4,5] var doubled = [] for(var i = 0; i < numbers.length; i++) { var newNumber = numbers[i] * 2 doubled.push (newNumber) } console.log (doubled) //=> [2,4,6,8,10]
声明式:
var numbers = [1,2,3,4,5] var doubled = numbers.map (function (n) { return n * 2 }) console.log (doubled) //=> [2,4,6,8,10]
什么是面向对象编程?
面向对象程序设计(英语:Object-oriented
programming,缩写:OOP)是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。
面向对象主要特征:封装性、继承性、多态性
面向对象编程具有以下优点:
易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
易扩展
通过继承,我们可以大幅减少多余的代码,并扩展现有代码的用途;
我们可以在标准的模块上(这里所谓的”标准”指程序员之间彼此达成的协议)构建我们的程序,而不必一切从头开始。这可以减少软件开发时间并提高生产效率;
模块化
封装可以定义对象的属性和方法的访问级别,通过不同的访问修饰符对外暴露安全的接口,防止内部数据在不安全的情况下被修改。这样可以使程序具备更高的模块化程度,方便后期的维护和修改。
同时,面向对象语言允许一个对象的多个实例同时存在,而且彼此之间不会相互干扰;
方便建模
虽然面向对象语言中的对象与现实生活中的对象并不是同个概念,但很多时候,往往可以使用现实生活中对象的概念抽象后稍作修改来进行建模,这大大方便了建模的过程。(但直接使用现实中的对象来建模有时会适得其反)。
JS判断数据类型的各种方法?
https://blog.csdn.net/mozuncangtianbaxue/article/details/77151598
①typeof
typeof是一个运算符,它返回一个用来表示表达式的数据类型的字符串。
语法:typeof(表达式)和typeof 变量名,第一种是对表达式做运算,第二种是对变量做运算。语法中的圆括号是可选项
typeof的运算数未定义,返回的就是 “undefined”.
运算数为数字 typeof(x) = “number”
字符串 typeof(x) = “string”
布尔值 typeof(x) = “boolean”
对象,数组和null typeof(x) = “object”
函数 typeof(x) = “function”
特殊情况:
- 对于基本类型,除 null 以外,均可以返回正确的结果。
- 对于引用类型,除 function 以外,一律返回 object 类型。
- 对于 null ,返回 object 类型。
- 对于 function 返回 function 类型。
②instanceof
instanceof运算符用来判断一个构造函数的prototype属性所指向的对象是否存在另外一个要检测对象的原型链上。通常来讲,使用 instanceof 就是判断一个实例是否属于某种类型。
语法:obj instanceof Object;//true
③constructor
constructor 属性返回对创建此对象的数组函数的引用。
语法:object.constructor
细节问题:
- null和undefined是无效的对象,因此是不会有constructor存在的,这两种类型的数据需要通过typeof来判断。
- JS对象的constructor是不稳定的,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object
为什么变成了Object?
prototype被重新赋值的是一个{}, {}是new Object()的字面量,因此new Object()会将Object原型上的constructor传递给{},也就是Object本身。
因此,为了规范,在重写对象原型时一般都需要重新给constructor赋值,以保证实例对象的类型不被改写。
④Object.prototype.toString.call
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型。
返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。
需要注意的是,必须通过Object.prototype.toString.call来获取,而不能直接 new Date().toString(), 从原型链的角度讲,所有对象的原型链最终都指向了Object, 按照JS变量查找规则,其他对象应该也可以直接访问到Object的toString方法,而事实上,大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此要用call来强制执行Object的toString方法。
⑤jquery.type()
jQuery.type() === "undefined"
jQuery.type( /test/ ) ===
"regexp"
源码分析:
var class2type = {};
jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); });
type: function( obj ) {
// 当obj为undefined或null时,直接返回对应的字符串形式
if ( obj == null ) {
return obj + "";
}
// Support: Safari <= 5.1 (functionish RegExp)
// 当obj的类型为object或function时,执行class2type[ toString.call(obj) ]
// 如果是自己创建了数据类型的话,那么就会统一返回"object"
// 否则执行 typeof obj
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call(obj) ] || "object" :
typeof obj;
},
undefined 和 null 有什么区别
http://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html
javaScript权威指南: null 和 undefined 都表示“值的空缺”,你可以认为undefined是表示系统级的、出乎意料的或类似错误的值的空缺,而null是表示程序级的、正常的或在意料之中的值的空缺。
null表示代表“空值”,代表一个空对象指针
null的典型用法:
作为函数的参数,表示该函数的参数不是对象
作为对象原型链的终点
undefined表示"缺少值",就是此处应该有一个值,但是还没有定义
undefined的典型用法:
变量被声明了,但没有赋值时,就等于undefined
调用函数时,应该提供的参数没有提供,该参数等于undefined
对象没有赋值的属性,该属性的值为undefined
函数没有返回值时,默认返回undefined
请指出document load和document ready的区别?
https://zhidao.baidu.com/question/432820456183363324.html
DOM文档加载的步骤:
(1) 解析HTML结构。
(2) 加载外部脚本和样式表文件。
(3) 解析并执行脚本代码。
(4) 构造HTML DOM模型。
//ready
(5) 加载图片等外部文件。
(6) 页面加载完毕。
//load
Js 有哪些创建对象的方式?
https://www.cnblogs.com/zczhangcui/p/6389023.html
https://blog.csdn.net/zpcqdkf/article/details/80401999
①{}
②new Object()
③使用字面量
④工厂模式:
function createPerson(name, age) {
var o = new Object()
o.name = name;
o.age = age;
o.sayName = function() {
console.log(name)
}
return o;
}
var p1 = createPerson(...);
不足:每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源
⑤构造函数模式(constructor):
function Person(name, age) {
this.name = name
this.age = age
this.sayName = function() {
console.log(name)
}
}
var p1 = new Person(...);
与工厂模型相比,具有以下特点:
1.没有显示创建对象;
2.直接将属性和方法赋给了this对象;
3.没有return语句;
4.要创建新实例,必须使用new操作符;(否则属性和方法将会被添加到window对象)
5.可以使用instanceof操作符检测对象类型
不足:
构造函数模式隐试的在最后返回return this 所以在缺少new的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象,可以根据return this 的特性调用call或者apply指定this
构造函数内部的方法会被重复创建,不同实例内的同名函数是不相等的。可通过将方法移到构造函数外部解决这一问题,但面临新问题:封装性不好。
⑥原型模式(prototype):
function Person() {};
Person.prototype = {
name: 'xiaoming',
friend: ['xiaoli'],
sayName: function () {
aleri(this.name);
}
}
var p1 = new Person();
console.log(p1 instanceof Person); //true
console.log(p1.prototype.constructor); //Object
console.log(p1.constructor); //Object
p1.constructor不再指向Person,而是指向了Object。如果constructor很重要,则需要特意将其设为适当的值,如:
Person.prototype = {
constructor: Person,
name: 'xiaoming',
friend: ['xiaoli','xiaohong'],
sayName: function () {
aleri(this.name);
}
}
但是这种方式会导致constructor属性变成可枚举。
如果想设置为不可枚举的(默认不可枚举),可以使用Object.defineProperty(Person.prototype, “constructor”, {
enumerable: false,
value: Person
});
特点:实现了方法与属性的共享,可以动态添加对象的属性和方法。
不足:
1.对于包含基本值的属性可以通过在实例上添加一个同名属性隐藏原型中的属性。然后,对于包含引用数据类型的值来说,会导致问题;
2.没有办法创建实例自己的属性和方法,也没有办法传递参数。
⑦构造函数+原型模式:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person,
sayName: function () {
aleri(this.name);
}
}
特点:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
⑧动态原型模式:
function Person (name, age) {
this.name = name;
this.age = age;
if(typeof this.sayName != 'function') {
Person.prototype.sayName = function () {
alert(this.name);
};
};
}
特点:在第一次调用构造函数时,将方法赋给原型对象的相应属性,其他示例的处理方式同构造函数模式;
⑨Object.create()
⑩寄生构造函数模式:
特点:仅仅封装创建对象的代码,然后再返回新创建的对象,仍使用new
操作符调用;
稳妥构造函数模式:
特点:没有公共属性,只有私有变量和方法,以及一些get/set
方法,用以处理私有变量。
new一个对象经历了哪些步骤?
要创建person的实例,必须使用new操作符,以这种方式调用构造函数会经历4个步骤:
1、创建一个新对象
2、将构造函数的作用域(this)赋给新对象
3、执行构造函数中的代码
4、返回新对象
对 this 的理解?
http://www.ruanyifeng.com/blog/2018/06/javascript-this.html
this为作用域上下文
一般情况下函数执行时,this总是指向调用该函数的对象。
当使用箭头函数的时候,情况就有所不同了:箭头函数内部的 this 是词法作用域,由上下文确定。
使用场景:
①在全局环境中,this 永远指向 window。
普通函数在调用时候(注意不是构造函数,前面不加 new),其中的 this 也是指向 window
匿名函数中的this指向全局对象window
setInterval和setTimeout定时器中的this指向全局对象window
②构造函数:如果函数作为构造函数使用,那么其中的 this 就代表它即将 new 出来的对象。
③方法调用:如果函数作为对象的方法时,方法中的 this 指向该对象。
④构造函数 prototype 属性:在整个原型链中,this 代表的也是当前对象的值。
⑤call/apply/bind调用:当一个函数被 call、apply 或者 bind 调用时,this 的值就取传入的对象的值。
⑥DOM绑定事件:在一个 HTML DOM 事件处理程序里,this 始终指向这个处理程序所绑定的 HTML DOM 节点
⑦箭头函数:当使用箭头函数的时候,情况就有所不同了:箭头函数内部的 this 是词法作用域。
函数执行时,this总是指向调用该函数的对象(即:判断this所在的函数属于谁)。
apply()、call()和 bind() 的区别及使用?
https://segmentfault.com/a/1190000018017796
三者的相同点:都是用来改变this的指向
call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别。
call 的语法:
Function.call(obj,[param1[,param2[,…[,paramN]]]])
注意点:
- 调用 call 的对象,必须是个函数 Function。
- call 的第一个参数,是一个对象。 Function 的调用者,将会指向这个对象。如果不传,则默认为全局对象 window。
- 第二个参数开始,可以接收任意个参数。每个参数会映射到相应位置的 Function 的参数上。但是如果将所有的参数作为数组传入,它们会作为一个整体映射到 Function 对应的第一个参数上,之后参数都为空。
apply 的语法:
Function.apply(obj[,argArray])
注意点:
- 调用 apply 的对象,必须是个函数 Function。
- 第二个参数,必须是数组或者类数组,它们会被转换成类数组,传入 Function 中,并且会被映射到 Function 对应的参数上。这也是 call 和 apply 之间,很重要的一个区别。
bind 方法也能改变函数体内的 this 指向。不同的是,bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。
bind语法:
Function.bind(thisArg[, arg1[, arg2[, ...]]])
使用场景:
call
对象的继承
判断数据类型
Object.prototype.toString.call();
将类数组变为数组
Array.prototype.slice.call(arguments);
apply
求数组最大值
let max = Math.max.apply(null, array);
实现两个数组合并
let arr1 = [1, 2, 3]; let arr2 = [4, 5, 6]; Array.prototype.push.apply(arr1, arr2);
new 操作符具体干了什么
https://cloud.tencent.com/developer/article/1195936?from=10680
https://www.jianshu.com/p/e7015984f608
https://www.jianshu.com/p/db4ae33d1e53
// ①新建一个空对象obj
let obj ={}; // ②obj的__proto__属性指向原型对象 obj.__proto__ = A.prototype; // ③将构造函数的this绑定obj,传入构造函数的参数,并将返回结果赋值给result let result = A.apply(obj, arguments); // ④如果result存在且result是对象或者函数,则构造函数返回result,否则将返回obj return (result && (typeof(result) === 'object' || typeof(result) === 'function')?result:obj);
使用关键字new创建新实例对象经过了以下几步:
1、创建一个新对象,如:var person = {};
2、新对象的proto属性指向构造函数的原型对象。
3、将构造函数的作用域赋值给新对象。(也所以this对象指向新对象)
4、执行构造函数内部的代码,将属性添加给person中的this对象。
5、返回新对象person。
var person = {};
person._proto_ = Person.prototype;//引用构造函数的原型对象
Person.call(person);//将构造函数的作用域给person,即:this值指向person Function.methos("new", function () { //新创建一个对象,它继承了构造器的原型对象。 var that = Object.create(this.prototype); //此时,this是指向Function构造器的。 //调用构造器,绑定this对象到新对象that上 var other = this.apply(that, argument); //此时,this对象指向that对象。 //如果它的返回值不是一个对象,就返回新的对象。 return (typeof other === "object" && other) || that; });
先看代码
var Func = function(){ };
var func = new Func ();
new共经过了4几个阶段
1、创建一个空对象
var obj = new Object();
2、设置原型链
obj.__proto__ = Func.prototype;
3、让Func中的this指向obj,并执行Func的函数体。
var result = Func.call(obj);
4、判断Func的返回值类型:
如果是值类型,返回obj。如果是引用类型,就返回这个引用类型的对象。
if (typeof(result) == "object"){
func = result;
} else{
func = obj;
}
可以看出new省略了哪几步:
1.不用手动新建一个obj ,new会帮你创建
2.不用把新建的obj的__proto__指向构造函数Common的prototype,new会帮你做。
3.构造函数this的作用域会指向实例本身。
4.不用手动return新建的obj,new会帮你return。
5.new出来的实例的__proto__会指向构造函数的prototype。构造函数的方法,实例可以直接调用。
JS如何实现对对象的拷贝(浅拷贝与深拷贝)?
https://blog.csdn.net/yaodebian/article/details/82778403
JS中对象分为基本类型和复合(引用)类型,基本类型存放在栈内存,复合(引用)类型存放在堆内存。
堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量。
- 浅拷贝:浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响;
- 深拷贝:在堆中重新分配内存,并且把源对象所有属性都进行新建拷贝,以保证深拷贝的对象的引用图不包含任何原有对象或对象图上的任何对象,拷贝后的对象与原来的对象是完全隔离,互不影响;
浅拷贝分两种情况,一是直接拷贝源对象的引用,二是源对象拷贝实例,但其属性(类型为Object、Array的属性)拷贝引用。
源对象拷贝实例,其属性对象拷贝应用
这种情况下,外层源对象是拷贝实例,如果其属性元素为复杂数据类型时,内层元素拷贝引用。
对于对象实例的拷贝,常用的方法有:Array.prototype.slice(), Array.prototype.concat(), jQuery的$.extend({},obj)和ES6中的拓展运算符(...)
例:
var a = [{c:1}, {d:2}];
var b = a.slice();
console.log(a === b); // 输出false,说明外层数组拷贝的是实例
a[0].c = 3;
console.log(b[0].c); // 输出 3,说明其元素拷贝的是引用
深拷贝方法:
①JSON.parse(JSON.stringify(目标对象); ( 即JSON.parse()和JSON.stringify() )
var a = {...};
var temp = JSON.stringify(a);
var b = JSON.parse(temp);
不足:只能拷贝符合JSON数据标准类型的对象,对于包含有function或者正则类型值解析时会报错
②使用递归
var clone = function (obj) {
if (obj === null) return null;
if (typeof obj !=== 'object') return obj;
if (obj.custructor === Date) return new Date(obj);
var newObj = new obj.constructor(); // 保持继承链
for (var key in obj) {
if (obj.hasOwnProperty(key)) { //不遍历其原型链上的属性
var val = obj[key];
//使用arguments.callee解除与函数名的耦合
newObj[key] = typeif val === 'object' ? arguments.callee(val) : val;
}
}
return newObj;
}
这里有三点需要注意:
1、用new obj.constructor ()构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承;
2、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。
3、上面的函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee。
③lodash —— _.cloneDeep()
很好地兼容了ES6的新引用类型,而且处理了环型对象的情况
JS如何实现继承?
http://www.php.cn/js-tutorial-411754.html
JS中的类型转换规则?
https://cloud.tencent.com/developer/article/1186778
转换为字符型方法:
1、利用引号(“”)转换为字符型
2、利用函数String();转换为字符型 var num=10; console.log(String(num));//输出为字符型
布尔型:true和false
1、利用!!转换为布尔型 var num=20; console.log(!!num)
2、利用Boolean()转换
3、var num="0"; num++; console.log(num);//1
var num="0"; num=num+1; console.log(num);//01
转换为数值型
1、-、*、/可以强制转换
2、利用Number();
parseInt(10,2)//2(将10这个二进制转换为十进制数) parseInt(值,进制数)、parseFloat()
NAN,不是一个数字
什么是闭包?
https://cloud.tencent.com/developer/article/1054445
https://cloud.tencent.com/developer/article/1390165
https://cloud.tencent.com/developer/article/1009285
https://cloud.tencent.com/developer/article/1407782
remember 词法作用域的基础规则:函数被执行时(executed)使用的作用域链(scope chain)是被定义时的scope chain,而不是执行时的scope chain
闭包:其实就是将函数内部和函数外部连接起来的一座桥梁,可以让函数外部的代码访问函数内容变量,可以把闭包简单理解成“定义在一个函数内部的函数”
function Person() {
var name = 'hello'
function say () {
console.log(name)
}
return say()
}
Person() // hello
由于 JavaScript 特殊的作用域,函数外部无法直接读取内部的变量,内部可以直接读取外部的变量,从而就产生了闭包的概念
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
用途:
最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
使用场景:
闭包经典使用场景一:通过循环给页面上多个dom节点绑定事件
闭包使用场景二:封装变量和方法
闭包使用场景三:延续局部变量的寿命
闭包使用场景四:采用函数引用方式的setTimeout调用
闭包使用场景五:将函数关联到对象的实例方法
注意点:
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露
介绍一下 JavaScript 原型?原型链?它们有何特点
(1)原型
首先明确一点,JavaScript是基于原型的
每个构造函数(constructor)都有一个原型对象(prototype),原型对象都包含一个指向构造函数的指针,而实例(instance)都包含一个指向原型对象的内部指针。
(2)原型链
JavaScript中所有的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有自己的原型对象,这样层层上溯,就形成了一个类似链表的结构,这就是原型链。
所有原型链的终点都是Object函数的prototype属性。Objec.prototype指向的原型对象同样拥有原型,不过它的原型是null,而null则没有原型
1.JS中每个函数都存在有一个原型对象属性prototype。并且所有函数的默认原型都是Object的实例。
2.原型链,简单理解就是原型组成的链,对象的__proto__它的是原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,就这样可以一直通过__proto__想上找,这就是原型链,当向上找找到Object的原型的时候,这条原型链就算到头了。
instanceof是判断实例对象的__proto__和生成该实例的构造函数的prototype是不是引用的同一个地址。
3.原型链实现了继承。原型链存在两个问题:
(1)、包含引用类型值的原型属性会被所有实例共享。
(2)、 在创建子类型时,无法向超类型的构造函数中传递参数。
Ajax是什么? 如何创建一个Ajax?
ajax的全称:AsynchronousJavascript And XML。
异步传输+js+xml。
所谓异步,在这里简单地解释就是:向服务器发送请求的时候,我们不必等待结果,而是可以同时做其他的事情,等到有了结果它自己会根据设定进行后续操作,与此同时,页面是不会发生整页刷新的,提高了用户体验。
(1)创建XMLHttpRequest对象,也就是创建一个异步调用对象
(2)创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息
(3)设置响应HTTP请求状态变化的函数
(4)发送HTTP请求
(5)获取异步调用返回的数据
(6)使用JavaScript和DOM实现局部刷新
同步和异步的区别?怎么异步加载 JavaScript?
(1)
同步:浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,j进行下一步操作。
异步:浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容。
(2)
动态添加 script 标签
defer
async
defer属性和async都是属于 script 标签上面的属性,两者都能实现 JavaScript 的异步加载。不同之处在于:async 在异步加载完成的时候就马上开始执行了,defer 会等到 html 加载完毕之后再执行
什么是内存泄漏?哪些操作会造成内存泄漏?
http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏:是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束
可能造成内存泄漏的操作:
1. 意外的全局变量
2. 闭包
3. 控制台日志
4. 循环引用(在两个对象彼此引用且彼此保留时,就会产生一个循环)
5. DOM清空或删除时,时间未清除导致的内存泄漏
6. 被遗忘的定时器或者回调函数
7. setTimeout 的第一个参数使用字符串而非函数的话,会引发内存泄漏。
谈谈垃圾回收机制方式及内存管理
回收机制方式
1、定义和用法:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
2、原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
3、实例如下:
function fn1() {
var obj = {name: 'hanzichi', age: 10};
}
function fn2() {
var obj = {name:'hanzichi', age: 10};
return obj;
}
var a = fn1();var b = fn2();
fn1中定义的obj为局部变量,而当调用结束后,出了fn1的环境,那么该块内存会被js引擎中的垃圾回收器自动释放;在fn2被调用的过程中,返回的对象被全局变量b所指向,所以该块内存并不会被释放。
垃圾回收策略:标记清除(较为常用)和引用计数。
标记清除:
定义和用法:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:"离开环境"。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
引用计数:
定义和用法:引用计数是跟踪记录每个值被引用的次数。
基本原理:就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。
什么时候触发垃圾回收?
垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。
IE6的垃圾回收是根据内存分配量运行的,当环境中的变量,对象,字符串达到一定数量时触发垃圾回收。垃圾回收器一直处于工作状态,严重影响浏览器性能。
IE7中,垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作。
2、合理的GC方案:(1)、遍历所有可访问的对象; (2)、回收已不可访问的对象。
3、GC缺陷:(1)、停止响应其他操作;
4、GC优化策略:(1)、分代回收(Generation GC);(2)、增量GC
AMD(Modules/Asynchronous-Definition)、CMD(Common ModuleDefinition)规范区别?
AsynchronousModule Definition,异步模块定义,所有的模块将被异步加载,模块加载不影响后面语句运行。所有依赖某些模块的语句均放置在回调函数中。
区别:
1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
2. CMD 推崇依赖就近,AMD 推崇依赖前置。
对AMD和CMD的理解,它们有什么区别
AMD和CMD都是为了解决浏览器端模块化问题而产生的,AMD规范对应的库函数有 Require.js,CMD规范是在国内发展起来的,对应的库函数有Sea.js
AMD和CMD最大的区别是对依赖模块的执行时机处理不同
1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
什么冒泡与捕获?事件委托的原理是什么?它的使用场景有哪些?
https://www.cnblogs.com/diver-blogs/p/5649270.html
事件冒泡:当一个子元素的事件被触发的时候(如onclick事件),该事件会从事件源(被点击的子元素)开始逐级向上传播,触发父级元素的点击事件。
事件委托:即是事件代理:事件委托就是利用事件冒泡,只制定一个时间处理程序,就可以管理某一类型的所有事件。
将事件处理器绑定到一个父级元素上,避免了频繁的绑定多个子级元素,依靠事件冒泡机制与事件捕获机制,子级元素的事件将委托给父级元素。
原理:利用事件冒泡和事件捕获机制实现的
优点:只需要将同类元素的事件委托给父级或者更外级的元素,不需要给所有元素都绑定事件,减少内存空间占用,提升性能; 动态新增的元素无需重新绑定事件
阻止事件冒泡:
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
阻止元素默认的行为(如链接的跳转、表单的提交):
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
拖拽会用到哪些事件被拖拽元素
dragstart 按下鼠标键并开始移动鼠标时
drag 在dragstart事件之后,在元素被拖动期间会持续触发该事件
dragend 当拖动停止时,会触发dragend事件
放置目标元素
dragenter 有元素被拖动到放置目标上
dragover 紧随dragenter发生,在被拖动的元素
还在放置目标范围内移动时,会持续触发该事件
dragleave 在元素被拖出放置目标时触发
drop 元素被放到了放置目标中触发
注释:拖拽发生过程:dragstart->drag->dragenter->dragover->dragleave/drop->dragend
Javascript中的定时器有哪些?他们的区别及用法是什么?
setTimeout 只执行一次
setInterval 会一直重复执行
如何使用ajax?
第一步,创建xmlhttprequest对象,var xmlhttp =new XMLHttpRequest();XMLHttpRequest对象用来和服务器交换数据。
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest(); //现代主流浏览器
} else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP"); // 针对浏览器,比如IE5或IE6
}
第二步,使用xmlhttprequest对象的open()和send()方法发送资源请求给服务器。
第三步,使用xmlhttprequest对象的responseText或responseXML属性获得服务器的响应。
第四步,onreadystatechange函数,当发送请求到服务器,我们想要服务器响应执行一些功能就需要使用onreadystatechange函数,每次xmlhttprequest对象的readyState发生改变都会触发onreadystatechange函数
json和xml的区别?
https://www.cnblogs.com/SanMaoSpace/p/3139186.html
JSON的定义
一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为。
XML的定义
扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。
- 数据体积方面,JSON相对XML来讲,数据的体积小,传递的速度更快些
- 数据交互方面,JSON与JavaScript的交互更加方便,更容易解析处理,更好地进行数据交互
- 数据描述方面,JSON对数据的描述性比XML较差
- 传输速度方面,JSON的速度要远远快于XML
json字符串如何转json对象JSON.stringify(),json对象如何转json字符串JSON.parse()
请解释JSONP的工作原理,以及它为什么不是真正的AJAX?
https://segmentfault.com/a/1190000009742074
https://www.cnblogs.com/soyxiaobi/p/9616011.html
JSONP (JSON with Padding)是一个简单高效的跨域方式,HTML中的script标签可以加载并执行其他域的javascript,于是我们可以通过script标记来动态加载其他域的资源。例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在 pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。
AJAX是不跨域的,而JSONP是一个是跨域的,还有就是二者接收参数形式不一样!
怎样添加、移除、移动、复制、创建和查找节点?
1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
2)添加、移除、替换、插入
appendChild() //添加
removeChild() //移除
replaceChild() //替换
insertBefore() //插入
3)查找
getElementsByTagName() //通过标签名称
getElementsByName() //通过元素的Name属性的值
getElementById() //通过元素Id,唯一性
有多个请求第一个完成后才执行第二,第二个完成后才执行第三个,你有几种方法实现?
方法一:设置ajax的async:false
方法二:用es6的promise
如何防止表单重复提交?有几种方法?
方法一:定义一个flag=false,改变flag的值
方法二:设置button的禁用属性
方法三:利用session
WEB应用从服务器主动推送Data到客户端有那些方式?
html5提供的Websocket
不可见的iframe
WebSocket通过Flash
XHR长时间连接
XHR Multipart Streaming
<script>标签的长时间连接(可跨域)
js设计模式
总体来说设计模式分为三大类:
1、创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
2、结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
3、行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模
window的onload事件和domcontentloaded的区别
https://www.cnblogs.com/yud123/p/7597025.html
1、当 onload
事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
2、当 DOMContentLoaded
事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。
jQuery经常使用$(document).ready();或者$(function(){}) 这都是使用了DOMContentLoaded事件。
JS中的arguments对象
https://cloud.tencent.com/developer/article/1395394
(1)由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。
(2)arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
(3)arguments很像数组,但它是一个对象,将其转换为数组有两种方法:
slice方法和逐一填入新数组。
![](https://i-blog.csdnimg.cn/blog_migrate/27150caaf6802a53d100e2dec5dbdd41.png)
(4)arguments对象有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。但在严格模式下,访问这个属性会抛出TypeError错误
JS中的作用域和作用域链
什么是作用域
作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。
什么是作用域链
如果父级也没呢?再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是 作用域链 。
实现页面加载进度条
1.给页面中重要节点后添加js代码,改变进度条显示(以显示在页面头部的进度条为例)
2.实时监控页面中加载情况,显示响应百分比(页面加载进度慢,一般都是图片、视频、音频较多,以下以图片为例,根据图片加载进度显示页面加载百分比)
3.document.onreadystatechange监控加载状态
说明:document.onreadystatechange(页面加载状态改变的事件);
document.readyState:
状态值:uninitialized(还未开始载入),loading(载入中),interactive(已加载,文档与用户可以开始交互),complete(载入完成)。
函数式编程
https://cloud.tencent.com/developer/article/1379522
函数式编程是一种强调以函数使用为主的软件开发风格。看到这句我想你还是一脸懵逼,不知道函数式编程是啥,不要着急,看到最后我相信你会明白的。
还有一点你要记住,函数式编程的目的是使用函数来抽象作用在数据之上的控制流和操作,从而在系统中消除副作用并减少对状态的改变。
总结
- 使用纯函数的代码绝不会更改或破坏全局状态,有助于提高代码的可测试性和可维护性
- 函数式编程采用声明式的风格,易于推理,提高代码的可读性。
- 函数式编程将函数视为积木,通过一等高阶函数来提高代码的模块化和可重用性。
- 可以利用响应式编程组合各个函数来降低事件驱动程序的复杂性(这点后面可能会单独拿一篇来进行讲解)。
怎么判断两个对象是否相等
https://www.jianshu.com/p/90ed8b728975
① 方法一:通过JSON.stringify(obj)
来判断两个对象转后的字符串是否相等
优点:用法简单,对于顺序相同的两个对象可以快速进行比较得到结果
缺点:这种方法有限制就是当两个对比的对象中key的顺序不是完全相同时会比较出错
②方法二:判断对象相等的步骤:
- 先判断俩者是不是对象
- 是对象后俩者长度是否一致
- 判断俩个对象的所有key值是否相等相同
- 判断俩个对象的相应的key对应的值是否相同
- 来一个递归判断里面的对象循环1-4步骤
-
function diff(obj1,obj2){
-
var o1 = obj1 instanceof Object;
-
var o2 = obj2 instanceof Object;
-
// 判断是不是对象
-
if (!o1 || !o2) {
-
return obj1 === obj2;
-
}
-
//Object.keys() 返回一个由对象的自身可枚举属性(key值)组成的数组,
-
//例如:数组返回下表:let arr = ["a", "b", "c"];console.log(Object.keys(arr))->0,1,2;
-
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
-
return false;
-
}
-
-
for (var o in obj1) {
-
var t1 = obj1[o] instanceof Object;
-
var t2 = obj2[o] instanceof Object;
-
if (t1 && t2) {
-
return diff(obj1[o], obj2[o]);
-
} else if (obj1[o] !== obj2[o]) {
-
return false;
-
}
-
}
-
return true;
-
}
如何让事件先冒泡后捕获
根据w3c标准,应先捕获再冒泡。若要实现先冒泡后捕获,给一个元素绑定两个addEventListener,其中一个第三个参数设置为false(即冒泡),另一个第三个参数设置为true(即捕获),调整它们的代码顺序,将设置为false的监听事件放在设置为true的监听事件前面即可。
如何获取当前日期
/**
*获取当前时间
*format=1精确到天
*format=2精确到分
*/
function getCurrentDate(format) {
var now = new Date();
var year = now.getFullYear(); //得到年份
var month = now.getMonth();//得到月份
var date = now.getDate();//得到日期
var day = now.getDay();//得到周几
var hour = now.getHours();//得到小时
var minu = now.getMinutes();//得到分钟
var sec = now.getSeconds();//得到秒
month = month + 1;
if (month < 10) month = "0" + month;
if (date < 10) date = "0" + date;
if (hour < 10) hour = "0" + hour;
if (minu < 10) minu = "0" + minu;
if (sec < 10) sec = "0" + sec;
var time = "";
//精确到天
if(format==1){
time = year + "-" + month + "-" + date;
}
//精确到分
else if(format==2){
time = year + "-" + month + "-" + date+ " " + hour + ":" + minu + ":" + sec;
}
return time;
}
alert(getCurrentDate(2));
立即执行函数和使用场景
https://www.jianshu.com/p/b10b6e93ddec
iframe的优缺点有哪些
1.iframe能够原封不动的把嵌入的网页展现出来。
2.如果有多个网页引用iframe,那么你只需要修改iframe的内容,就可以实现调用的每一个页面内容的更改,方便快捷。
3.网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用iframe来嵌套,可以增加代码的可重用。
4.如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由iframe来解决。
iframe的缺点:
1.会产生很多页面,
不容易管理
。
2.iframe框架结构有时会让人感到迷惑,如果框架个数多的话,可能会出现上下、左右滚动条,会分散访问者的注意力,
用户体验度差
。
3.代码复杂,无法被一些搜索引擎索引到,这一点很关键,现在的搜索引擎爬虫还不能很好的处理iframe中的内容,所以使用iframe会
不利于搜索引擎优化
。
4.很多的移动设备(PDA手机)无法完全显示框架,
设备兼容性
差。
5.iframe框架页面会
增加服务器的http请求
,对于大型网站是不可取的。
分析了这么多,
现在基本上都是用Ajax来代替iframe,所以iframe已经渐渐的退出了前端开发
。
服务端渲染(SSR)与客户端渲染(CSR)
服务端渲染(吐)
服务端在返回 html 之前,在特定的区域,符号里用数据填充,再给客户端,客户端只负责解析 HTML 。也被称为 fat-server, thin-client 模式
客户端渲染(填)
html 仅仅作为静态文件,客户端端在请求时,服务端不做任何处理,直接以原文件的形式返回给客户端客户端,然后根据 html 上的 JavaScript,生成 DOM 插入 html。也被称为 fat-client, thin-server 模式
渲染本质一样,都是字符串拼接,将数据渲染进一些固定格式的html代码中形成最终的html展示在用户页面上。
拼接字符串必然引起性能的消耗。
服务端渲染性能消耗在服务端,当用户量比较多时,缓存部分数据以避免过多数据重复渲染。
客户端渲染,如当下火热的 spa 框架,Angular、React、Vue,在首次渲染时,大多是将原 html 中的数据标记(如 {{ text }} )替换。客户端渲染较难的一点是数据更新以后,页面响应式更新时如何节省资源,直接 DOM 的读写,是很消耗性能的。 Vue 2.0 + 有 Vnode,进行 diff 后,渲染到页面上。
利弊
var的变量提升底层原理是什么?
https://www.cnblogs.com/wancheng7/p/8306711.html
js自上而下的执行过程分为两个词法分析和执行两个阶段:词法分析主要包括:分析形参、分析变量声明、分析函数声明三个部分.通过词法分析将我们写的js代码转成可以执行的代码,接下来才是执行。
变量提升还有一种情况,就是函数,词法分析的时候关于函数声明的处理与变量声明的处理不太一致,会一步到位的给当前函数活动对象增加对应函数名的属性,并重写该方法。也就是不会像变量那样先赋值undefined了。
创建对象的三种方式
- 通过对象直接量
- 通过new创建对象
- 通过Object.create()
new和Object.create的区别
在 JavaScript 中,构造函数只是一些使用 new 操作符时被调用的函数。
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。
1. 创建(或者说构造)一个全新的对象。
2. 这个新对象会被执行 [[ 原型 ]] ([[Prototype]])连接。
3. 这个新对象会绑定到函数调用的 this 。
4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
调用Object.create(..) 会凭空创建一个“新”对象并把新对象内部的 [[Prototype]] 关联到你指定的对象。
哪种情况下__proto__和prototype的指向是同一个?
一个实例对象的__proto__属性指向自身构造函数的原型链,即:
Function.__proto__===Function.prototype;//true
其他的,比如手动修改__proto__也可以做到,这种就没办法判断。
varA=function(){}
A.__proto__=A.prototype
A.__proto__===A.prototype;//true
typeof array null undefined NaN分别是什么
typeof 运算符返回一个用来表示表达式的数据类型的字符串。
可能的字符串有:”number”、”string”、”boolean”、”object”、”function” 和 “undefined”。
1.类型分析:
js中的数据类型有undefined,boolean,number,string,object等5种,前4种为原始类型,第5种为引用类型。
var a1;
var a2 = true;
var a3 = 1;
var a4 = “Hello”;
var a5 = new Object();
var a6 = null;
var a7 = NaN;
var a8 = undefined;
alert(typeof a); //显示”undefined”
alert(typeof a1); //显示”undefined”
alert(typeof a2); //显示”boolean”
alert(typeof a3); //显示”number”
alert(typeof a4); //显示”string”
alert(typeof a5); //显示”object”
alert(typeof a6); //显示”object”
alert(typeof a7); //显示”number”
alert(typeof a8); //显示”undefined”
从上面的代码中可以看出未定义的值和定义未赋值的为undefined,null是一种特殊的object,NaN是一种特殊的number。
2.比较运算
var a1; //a1的值为undefined
var a2 = null;
var a3 = NaN;
alert(a1 == a2); //显示”true”
alert(a1 != a2); //显示”false”
alert(a1 == a3); //显示”false”
alert(a1 != a3); //显示”true”
alert(a2 == a3); //显示”false”
alert(a2 != a3); //显示”true”
alert(a3 == a3); //显示”false”
alert(a3 != a3); //显示”true”
从上面的代码可以得出结论:(1)undefined与null是相等;(2)NaN与任何值都不相等,与自己也不相等。Null 数据类型
把undefined和null转成Number分别是什么
NAN和0
六种数据类型转Number规则:
1、Number转Number,本来多少就是多少;
2、String转Number:数字字符串转成对应数字,空字符串转为0,其他均为NaN;
3、Boolean转Number:true为1,false为0;
4、null为0,undefined为NaN;
5、Object(包括对象、函数、数组、正则等)转Number调用其valueof()方法,如果为NaN,调用toString()方法,如果还是NaN,则结果为NaN。
instanceOf和constructor的区别
instanceof 二元运算符
返回一个布尔值,该值指示一个对象是否为特定类的一个实例。
语法: object instanceof class
instanceof
运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype
属性。通俗来将就是判断一个实例对象是否由某个构造函数构造而来。
nstanceof不仅可以判断实例对象直接的构造函数,而且还能判断原型链上所有的构造函数。
constructor 属性,存在于Object.prototype的属性
返回对象的构造函数,
语法: object.constructor
返回值是函数的引用,不是函数名:
我们创建的每个函数都有一个prototype(原型)对象,这个属性是一个指针,指向一个对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性是一个指向prototype属性所在函数的指针。
如何获取元素的父节点和兄弟节点
https://www.jb51.net/article/143286.htm
JS如何获得用户来源(navigator.userAgent)
Navigator userAgent 属性
定义和用法
userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值。
语法
navigator.userAgent
以下代码可以判断目前大部分浏览器类别
var browser = {
versions: function() {
var u = navigator.userAgent,
app = navigator.appVersion;
return {
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android终端
iPhone: u.indexOf('iPhone') > -1, //是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
weixin: u.indexOf('MicroMessenger') > -1, //是否微信 (2015-01-22新增)
qq: u.match(/\sQQ/i) == " qq" //是否QQ
};
}(),
language: (navigator.browserLanguage || navigator.language).toLowerCase()
}
Ajax返回204算是成功吗
204请求成功了,但是没有结果返回来。
205 Reset Content, 表示执行成功, 重置页面(Form表单).
总的来说, 204适合多次对一个Item进行更新, 而205则适合多次提交一个系列的Item.
但, 请注意, 目前还没有一个浏览器支持205, 大部分的浏览器, 都会把205当做204或者200同样对待.
栈和队列有什么区别,具体的应用场景
1.是什么?
栈和队列其实是一个工具,他们传统的工具方法 工具类不同,他们是“思想”工具,大家都知道,栈是后进先出,队列是先进先出。但是实际怎么用却不知道。
2.怎么用?
栈和队列是很好的工具,他们的应用我们平时开发可能用到得少,但是在实际中,他们的应用非常多。
栈:栈先进先出的特点,可以很好的控制访问控制,栈的数据访问是有很严格的,只能访问最后加入的数据,这对数据访问控制严格的应用很有好处。现实中,字符串倒序输出,使用栈的原理就可以很好的实现。
队列:队列可以模拟很多现实的生产环境,例如排队,队列是先进先出,不允许有任何元素插队,这对于解决现实生产问题有很大帮助。
优点:栈和队列的操作的时间复杂度都是O(1),效率非常高。
Ajax如何实现的
https://www.jianshu.com/p/8eed5f8291ed
preventDefault()、stopPropagation()、return false之间的区别
preventDefault():阻止事件默认行为;
stopPropagation():阻止事件冒泡;
return false:当你每次调用”return false“的时候,它实际上做了3件事情:
•event.preventDefault();
•event.stopPropagation();
•停止回调函数执行并立即返回。
页面共享数据的方法有哪些
1.表单隐藏域;
2.url传递;
3.storage;
4.vue-route;
5.vuex
js如何实现一个栈
栈,是一种特殊的线性表,其插入及删除的操作都在线性表的同一端进行。这一端称为栈顶,另一端称为栈底。就类似于餐厅里的一摞盘子,后放的盘子在上方,也会先被人拿走。栈具有“后进先出”的逻辑特性。栈在计算机科学中有着广泛的应用,递归函数的实现就利用了栈这种数据结构,在递归时,计算机会维护一个递归工作栈,当一个递归函数被调用时,被调函数的局部变量、形参的值以及一个返回地址就会储存在递归工作栈中。运行时按照后进先出的顺序,进行函数执行,完成递归操作。编译原理中也多次使用栈这种数据结构~
栈是一种特殊的线性表,故其在存储结构上也有链式存储和顺序存储两种。代码如下:
- /*链栈的JS实现*/
- function LinkedStack(){
- //节点结构定义
- var Node = function(element){
- this.element = element;
- this.next = null;
- }
- var length = 0,
- top; //栈顶指针
- //压栈操作
- this.push = function(element){
- var node = new Node(element),
- current;
- if(!top){
- top = node;
- length++;
- return true;
- }else{
- node.next = top;
- top = node;
- length++;
- return true;
- }
- }
- //退栈操作
- this.pop = function(){
- var current = top;
- if(top){
- top = current.next;
- current.next = null;
- length--;
- return current;
- }else{
- return 'null stack';
- }
- }
- //获取栈顶节点
- this.top = function(){
- return top;
- }
- //获取栈长
- this.size = function(){
- return length;
- }
- this.toString = function(){
- var string = '',
- current = top;
- while(current){
- string += current.element;
- current = current.next;
- }
- return string;
- }
- //清空栈
- this.clear = function(){
- top = null;
- length = 0;
- return true;
- }
- }
- //顺序栈的JS实现 这里直接使用了JS内置的Array对象
- function ArrayStack(){
- var arr = [];
- //压栈操作
- this.push = function(element){
- arr.push(element);
- }
- //退栈操作
- this.pop = function(){
- return arr.pop();
- }
- //获取栈顶元素
- this.top = function(){
- return arr[arr.length-1];
- }
- //获取栈长
- this.size = function(){
- return arr.length;
- }
- //清空栈
- this.clear = function(){
- arr = [];
- return true;
- }
- this.toString = function(){
- return arr.toString();
- }
- }
哈希表是怎么样的结构
https://www.cnblogs.com/yangecnu/p/Introduce-Hashtable.html
哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。
哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。
使用哈希查找有两个步骤:
- 使用哈希函数将被查找的键转换为数组的索引。在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突
- 处理哈希碰撞冲突。有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法和线性探测法。
哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。
说说操作系统的内存管理
https://blog.csdn.net/iam_lain/article/details/46816755
怎么样实现一个服务器
https://cloud.tencent.com/developer/article/1448267
https://cloud.tencent.com/developer/news/6596
假如访问A.com存进了一个cookie,在另外一个页面用ajax向A的域名发请求会携带cookie吗
ajax跨域传递cookie
https://blog.csdn.net/qq_29845761/article/details/51897705
一级域名相同,只是二级域名不同的情况下,浏览器允许通过设置document.domain共享Cookie。也就是说,Cookie只能跨二级域名来访问,不能跨一级域名来访问。
通过修改请求头是可以传递cookie等信息的。但是w3c的标准写的很清楚,cookie,connection和content-length等是不安全的字段,容易导致多种的request smuggling攻击,不允许编程设置。这些字段浏览器会自动帮你设置,如果设置就会报出错误:“Refused to set unsafe header "Content-Length"。
既然ajax跨域中直接设置请求头是不允许的,那么我们就必须在ajax请求发出之前就应该设置cookie,然后ajax请求时会自动去填充请求头header内容(其中cookie内容会自动从硬盘中读取)。同时服务器端也要做些返回头的修改:response.setHeader("Access-Control-Allow-Credentials","true");
localStorage存储数据格式是什么?怎么样把一个数组存进localSorage?
localStorage存储数据的格式都是以字符串的形式来存储的,
存储数组对象的方式就是将数组对象转为字符串,在读取的时候再将字符串转成数组对象
表单提交和ajax的区别
有如下几种区别:
1. Ajax在提交、请求、接收时,都是异步进行的,网页不需要刷新;Form提交则是新建一个页面,哪怕是提交给自己本身的页面,也是需要刷新的;
2. A在提交时,是在后台新建一个请求;F却是放弃本页面,而后再请求;
3. A必须要使用JS来实现,不启用JS的浏览器,无法完成该操作;F却是浏览器的本能,无论是否开启JS,都可以提交表单; 4. A在提交、请求、接收时,整个过程都需要使用程序来对其数据进行处理;F提交时,却是根据你的表单结构自动完成,不需要代码干预。
JS中的事件绑定、事件监听、事件委托是什么?
https://www.cnblogs.com/Tacklingpioneer/p/6433959.html
写一个API实现insertAfter
DOM里有insertBefore函数,但没有insertAfter函数,所以自己编写一个该函数:
Element.prototype.insertAfter=
function (newElement, targetElement){ var parent = targetElement.parentNode;//获取父级元素节点 if(parent.lastChild == targetElement){//如果目标元素是父级元素的最后一个子元素 parent.appendChild(newElement); }else{//如果不是最后一个子元素,则调用insertBefore函数 parent.insertBefore(newElement,targetElement.nextSibling); } }
JS的如何实现倒计时,为什么不准,校正方式
https://www.cnblogs.com/xiaochongchong/p/5982004.html
前端倒计时许考虑两个问题:
1.由于js是单线程的,也就是阻塞的,定时可定会不准。无论setTimeout()还是setInterval(),都有问题;
2、打开浏览器,然后切换到其他app,再次回到浏览器,这期间js可能停止执行的问题。
本质上来说,只需要两个时间点就可以了:当前时间、秒杀开始时间。有了这两个数据,我们就可以倒计时了。两个时间的差值就是我们要倒计时的时间差,每隔1秒减少1/每隔1毫秒减少1。但是,当前的时间不要用new Date(),这是获取手机的时间,如果用户修改手机的时间,这个会跟随变化的。基于此,当前的时间必须是服务端传过来的。用户每次打开网页,都会传服务器的当前时间。秒杀开始的时间一般是后台配置好的,只要配置了,他就定了。是个不变的量。
解决方法1:
以服务器的当前时间为倒计时的当前时间,上面提到需要考虑的那两个问题都没有解决,如果我们实时同步服务器的当前时间,那么这两个问题就都解决了。
如何时时同步呢?最先想到的是,时时调用一个后端的接口,这个接口返回服务器的当前时间。但实际上,我们只需要跟服务器ajax交互就可以了,请求一个服务器不存在的资源,我们从返回中拿返回头,再从这个头中取服务器的当前时间。
这种方法就是不断的同步服务器的事件。
以上代码的欠缺是每次请求服务器,性能不佳。但好处也显而易见,3个用户同时访问页面,他们的倒计时结果基本上一样的,秒杀变得比较公平。当服务器出现问题,请求不到服务器的当前时间,这个代码会有问题,但这不是我们考虑的问题。
解决方法2:
还有种解决的办法,就是计算每次倒计时的误差,你能知道第n次你倒计时的应该发生的时间,再跟当前的时间对比,这个误差就在下次倒计时的时候考虑进去。这样可以保证误差不累计。但是对于退出浏览器的行为,这种其实是有问题的。
这种方法是不断校准倒计时的周期,而周期不可能为负数,治标不治本。
实现一个ajax,兼容
function ajax() {
var oBtn = document.getElementById('btn');
oBtn.onclick = function() {
/*
1.创建一个ajax对象
ie6以下new ActiveXObject('Microsoft.XMLHTTP')
*/
var xhr = null;
//第一种处理兼容方式
/*if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest(); //Firefox(或其他非IE)
} else {
xhr = new ActiveXObject('Microsoft.XMLHTTP'); //IE浏览器
}*/
//第二种处理兼容方式
try {
xhr = new XMLHttpRequest();
} catch (e) {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
/*
open方法
参数
1.打开方式
2.地址
3.是否异步
异步:非阻塞 前面的代码不会影响后面代码的执行
同步:阻塞 前面的代码会影响后面代码的执行
*/
xhr.open('get','links/1.txt',true);
//提交 发送请求
xhr.send();
//等待服务器返回内容
xhr.onreadystatechange = function() {
if ( xhr.readyState == 4 ) {
alert( xhr.responseText );
}
}
}
}
假设一个object A里面的值n为1,怎么样知道n改变了,有事件绑定吗
设置一个js变量
var obj = {};
监听方法
Object.defineProperty(obj,'data', {
get: function () {
return data;
},
set: function (newValue) {
data = newValue;
console.log(newValue);//成功触发方法打印出设置的值
}
});