JavaScript基础知识题
1.不一样的变量声明:const和let
ES6推荐使用let声明局部变量,相比之前的var(无论声明在何处,都会被视为声明在函数的最顶部)
let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了。
如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行。
有几个点需要注意:
- let 关键词声明的变量不具备变量提升(hoisting)特性
- let 和 const 声明只在最靠近的一个块中(花括号内)有效
- 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
- const 在声明时必须被赋值
2.模板字符串
在ES6之前,我们往往这么处理模板字符串:
通过“\”和“+”来构建模板
而对ES6来说
①基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
②ES6反引号(``)直接搞定;
3.箭头函数(Arrow Functions)
ES6 中,箭头函数就是函数的一种简写形式,使用括号包裹参数,跟随一个 =>,紧接着是函数体;
箭头函数最直观的三个特点。
- 不需要 function 关键字来创建函数
- 省略 return 关键字
- 继承当前上下文的 this 关键字
4. 函数的参数默认值
在ES6之前,我们往往这样定义参数的默认值:
// ES6之前,当未传入参数时,text = 'default'; function printText(text) { text = text || 'default'; console.log(text); } // ES6; function printText(text = 'default') { console.log(text); } printText('hello'); // hello printText();// default
5.Spread / Rest 操作符
Spread / Rest 操作符指的是 ...,具体是 Spread 还是 Rest 需要看上下文语境。
当被用于迭代器中时,它是一个 Spread 操作符:
function foo(x,y,z) { console.log(x,y,z); } let arr = [1,2,3]; foo(...arr); // 1 2 3
当被用于函数传参时,是一个 Rest 操作符:当被用于函数传参时,是一个 Rest 操作符:
function foo(...args) { console.log(args); } foo( 1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
6.二进制和八进制字面量
ES6 支持二进制和八进制的字面量,通过在数字前面添加 0o 或者0O 即可将其转换为八进制值:
let oValue = 0o10; console.log(oValue); // 8 let bValue = 0b10; // 二进制使用 `0b` 或者 `0B` console.log(bValue); // 2
7.对象和数组解构
// 对象 const student = { name: 'Sam', age: 22, sex: '男' } // 数组 // const student = ['Sam', 22, '男']; // ES5; const name = student.name; const age = student.age; const sex = student.sex; console.log(name + ' --- ' + age + ' --- ' + sex); // ES6 const { name, age, sex } = student; console.log(name + ' --- ' + age + ' --- ' + sex);
8.对象超类
ES6 允许在对象中使用 super 方法:
var parent = { foo() { console.log("Hello from the Parent"); } } var child = { foo() { super.foo(); console.log("Hello from the Child"); } } Object.setPrototypeOf(child, parent); child.foo(); // Hello from the Parent // Hello from the Child
9.for...of 和 for...in
for...of 用于遍历一个迭代器,如数组:
let letters = ['a', 'b', 'c']; letters.size = 3; for (let letter of letters) { console.log(letter); } // 结果: a, b, c
for...in 用来遍历对象中的属性:
let stus = ["Sam", "22", "男"]; for (let stu in stus) { console.log(stus[stu]); } // 结果: Sam, 22, 男
10.ES6中的类
ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。
函数中使用 static 关键词定义构造函数的的方法和属性:
class Student { constructor() { console.log("I'm a student."); } study() { console.log('study!'); } static read() { console.log("Reading Now."); } } console.log(typeof Student); // function let stu = new Student(); // "I'm a student." stu.study(); // "study!" stu.read(); // "Reading Now."
类中的继承和超集:
class Phone { constructor() { console.log("I'm a phone."); } } class MI extends Phone { constructor() { super(); console.log("I'm a phone designed by xiaomi"); } } let mi8 = new MI();
extends 允许一个子类继承父类,需要注意的是,子类的constructor 函数中需要执行 super() 函数。
当然,你也可以在子类方法中调用父类的方法,如super.parentMethodName()。
在 这里 阅读更多关于类的介绍。
有几点值得注意的是:
- 类的声明不会提升(hoisting),如果你要使用某个 Class,那你必须在使用之前定义它,否则会抛出一个 ReferenceError 的错误
- 在类中定义函数不需要使用 function 关键词
②简述JavaScript中的作用域链?学习地址
由于js存在全局变量和局部变量,在调用一个变量是,会对他的作用域链进行查找,如果函数内部定义了这个变量,那么取该变量的值,如果没有,那么向上一层查找,如果找到了,就获取这个值,如果还没找到,继续往上层查找,直到找到位置,如果找到最后也没找到,那么该变量的值为undefined。
关于变量作用域的知识,相信学习JavaScript的朋友们一定早已经接触过,这里简单列举:
- JavaScript中变量是以对象属性的形式存在的:全局变量是全局对象的属性;局部变量是声明上下文对象的属性。(声明上下文对象是一个对用户不可见的内部实现,无法被引用,每当函数调用便创建这个对象以存放局部变量)
- JavaScript虽然是解释型语言,但也存在预处理过程,其中便包含了声明提前。JavaScript解释器运行前,会先将整个程序中的变量声明(包括函数)提前到作用域顶部执行。所以在程序中变量的使用语句可以出现在声明语句之前。
- JavaScript中没有块级作用域(像C语言那样用方括号{}划分作用域),而是使用函数作用域;
- 不在任何函数体内声明的变量为全局变量,拥有全局作用域,可以在程序的任何位置被访问;而在函数内声明的变量(包括函数的参数)为局部变量,拥有局部作用域,只在函数内部有定义;
- 若局部变量与全局变量重名,局部变量优先级高,可以遮盖全局变量;
-
局部作用域可以相互嵌套(因为函数可以嵌套)
而作用域链的概念,接触过的人就没那么多了。其实作用域链很好理解,上方第一条已经明确了,JavaScript中变量是以对象属性的形式存在的,而作用域链其实就是这些对象组成的一个链表。全局变量是全局对象的属性,局部变量是声明上下文对象的属性。这些对象按从内向外的顺序链接,链尾当然就是全局对象。当函数运行中需要查找某一个变量的时候,就会沿着作用域链依次查询每个对象是否拥有与该变量同名的属性,如果存在则直接使用;若整条链上都不存在这个属性,便抛出ReferenceError。 在函数定义的时候,便创建了作用域链,链上的对象顺序便确定下来,当函数被调用,创建相应的对象添加到作用域链中,作用域链会随着函数的定义调用而更新。在嵌套函数中更是如此,每次调用外部函数时,内部函数又会重新定义,随之带来的就是作用域链的相应变化。关于作用域链的数量,可以用树来比喻,全局对象是根节点,函数的嵌套代表着树节点的层级关系,那么一个程序中作用域链的数量等于对应树中叶子节点的数量。
③简述JS中创建自定义对象的方式?学习地址
1.对象直接量
var obj = {
webName: "蚂蚁部落",
address: "青岛市南区"
}
对象直接量是由键值对列表组成,列表包含在大括号中,属性名称和属性值由冒号分隔,每一个键值对之间是用逗号分隔,属性值可以是任何类型。
2.工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。考虑到在ECMAScript种无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如下面例子所示:
function createPerson(name,age,job){
var o=new Object();
o.name=name;
o.age=age;
o.job=job;
o.sayName=function(){
alert(this.name);
};
return o;
}
var person1=new createPerson("Nicholas",29,"Software Engineer");
var person2=new createPerson("Greg",27,"Doctor");
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person对象。可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个类似对象的问题,但是却没有解决对象识别的问题。随之来的就是构造函数模式。
3.构造函数模式 new object()
var obj = new object();
obj.webName = "蚂蚁部落";
obj.address = "青岛市南区";
以上代码采用构造函数方式创建对象obj,为其添加两个属性并赋值。
4.原型模式
在js中可以像“后端”一样使用原型模式创建对象。在调式的过程中,发现原型模式的确可以创建对象,而且相当于引用类型,也就是说,比如通过它来创建两个对象,那么这两个对象创建完后,若其中一个对象改变了原型模式中的属性的值,那么另一个对象也会受到影响,这也就是说不管创建多少个对象,那么这些对象都指向同一个存储空间,这也就是原型模式的一个优势,避免了资源的浪费,因为所有通过它创建的对象,都指向同一个内存空间。
5.组合使用构造函数模式和原型模式
④this关键字的作用是什么?学习地址
笼统的说,关键字this指向当前对象。比如,
顶级代码中的this指向全局对象;
在指定元素事件的时候,this指定当前发生事件的元素对象;
对于嵌套函数,如果嵌套函数作为方法被调用,其this指向调用它的对象;
如果作为函数调用,this是全局对象或者为undefined(严格模式下)。
⑤简要描述JS中的匿名函数?学习地址
匿名函数常用于定义不需要重复使用的函数,用完即释放。
用小括号括上,并在后面加一组小括号用来自调。
匿名函数的基本形式为(function(){...})();
前面的括号包含函数体,后面的括号就是给匿名函数传递参数并立即执行之。
匿名函数的作用是避免全局变量的污染以及函数名的冲突。
⑥什么是正则表达式?JS中如何应用?
正则表达式本身就是一个字符串,由一些普通字符和特殊字符组成,用以描述一种特定的字符规则的表达式。
正则表达式常用于在一段文本中搜索、匹配或替换特定形式的文本。
应用分为两种方式:
1.结合String对象的replace、search和match方法,实现对字符串的替换查找和匹配。
2.定义正则表达式对象,实现对字符串的复杂匹配操作。
⑦简述JS中定义函数的几种方式?学习地址
1.函数语句:即使用function关键字显式定义函数。
function f(x){
return x+1;
}
2.函数定义表达式,也称为"函数直接量"。
var f = function(x){return x+1;}
3.使用Function()构造函数定义。(必须大写F)
PS:如果一个函数是需要经常使用的函数就应该避免使用Function()构造函数来定义函数。Fuction()构造函数可以动态定义和编译函数,而function语句只能预编译函
数,因此在每次调用使用Function()构造函数定义的函数时,javascript都要编译一次函数。学习地址
⑧简述arguments对象的作用?
arguments可以访问函数的参数。即开发者在定义函数时,无需明确的为方法声明参数,也可以在方法体中使用arguments来访问参数。
这是因为arguments是一种特殊的对象,在函数代码中,表示函数的参数数组。
因此,可以通过使用arguments.length检测函数的参数个数,
可以通过下标arguments[index]来访问某个参数。
这样,可以用arguments对象判断传递给函数的参数个数并获取参数。适用于函数参数无法确定个数的情况。
⑨什么是逻辑短路?
逻辑短路是对于逻辑运算而言,是指,仅计算逻辑表达式的一部分便能确定结果,而不是对整个表达式计算的现象。
对于"&&"运算符,当第一个操作数为false时,将不会判断第二个操作数,因为结果一定是false。
对于"||"运算符,当第一个操作数为true时,将不会判断第二个操作数,因为结果一定是true。
⑩解释一下JS中局部变量和全局变量的区别?
全局变量拥有全局作用域,在JavaScript代码的任何地方都能访问;在函数内声明的变量只在函数体内有定义,即为局部变量,其作用域是局部性的。
需要注意的是,在函数体内声明局部变量时,如果不用var关键字,则将声明全局变量。
⑪简述JS的数据类型?
可分为基本类型和引用类型。
五大基本类型:string 、number 、boolean 、以及两个原始值: null(空) 和 undefined(未定义)
基本类型的访问是按值访问的,就是说你可以操作保存在变量中的实际的值。有如下特点:
- 基本数据类型的值是不可变的(我们不能给基本数据类型添加属性和方法);
- 基本数据类型的比较是值的比较(只有他们的值相等的时候才是相等的);
- 基本数据类型的变量是存放在栈里面的
引用类型(对象类型): Array 、Date 、Object 、RegExp 、Function
- 引用数据类型可以拥有属性和方法,且值是可变的;
- 引用数据类型的值是同时保存在栈内存和堆内存的对象;
⑫如何阻止表单提交?
在 onsubmit 事件中返回 return false;调用 event.preventDefault();效果一样。