1. 简述面向对象的设计思想。
面向对象会把事物抽象成对象的概念,系统中一切皆为对象,对象是属性和操作的封装体。面向对象得设计思想就是在解决问题得过程中,先抽象出对象,然后给对象添加属性和执行方法。
2. 简述什么是静态成员和实例成员。
静态成员:由构造函数直接访问到的属性和方法
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.say = function() { alert("这是静态方法"); };
Person.say 就是静态成员
Person.length 也是静态成员, 因为length是函数中的 方法, 是来直接获取函数中形参的个数。
实例成员:由构造函数创建出来的对象能直接访问的属性和方法,包括:对象本身 以及原型中的所有的属性和方法
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {
console.log("hello, rose");
};
var p = new Person("jack", 19);
(p.name p.age p.sayHi) 括号内三个就是实例成员了。
3. 使用原型对象时,如何设置对象的成员。
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log(`${this.uname}会唱歌`);
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
ldh.sing();//刘德华会唱歌
zxy.sing();//张学友会唱歌
4. 简单介绍面向对象编程中,自调用函数的作用。
- 自调用函数内部可以访问外部的变量,而外部环境不能访问内部的变量
- 内部定义的变量成为局部变量,不会污染全局变量
- 可以封装大量的对象方法,单独测试它们
5. 简述面向对象编程的思路。
比如解决一个问题:洗衣机有脏衣服,怎么洗干净。
1.区分'洗衣机'和'人'两个对象
2.给'洗衣机'添加属性和'洗衣服方法','烘干方法'
3.给'人'添加属性和'加洗衣液的方法','加水的方法'
4.执行:人.加洗衣粉-人.加水-洗衣机.洗衣服-洗衣机.烘干
6. 简述原型链查找过程。
每个函数都有一个prototype原型属性,指向一个对象,对象中包含特定类型和所以实例共享的属性和方法 在prototype原型属性中有一个自带的constructor属性,它储存的值是当前的函数本身 每个实例对象都有一个_proto_属性,这个属性指向当前实例的的prototype原型属性 原型链是基于__proto__向上查找的机制
- 当我们操作实例的某个属性或者方法的时候,首先找到自己空间中私有的属性或者方法,
- 找到了,则结束查找,使用自己的私有即可
- 没有找到 则基于__proto__ 找所属类的prototype,如果找到了就用这个公有的,如果没找到,基于原型上的__proto__继续向上查找
- 一直找到Object:prototype的原型为止,如果在没有,操作的属性或者方法不存在
7. 描述函数声明与函数表达式的区别。
- 函数声明必须有名字
- 函数声明会函数提升,在预解析阶段就已创建,声明前后都可以调用
- 函数表达式类似于变量赋值
- 函数表达式可以没有名字,例如匿名函数
- 函数表达式没有函数提升,在执行阶段创建,必须在表达式执行之后才可以调用
8. 描述call、apply和bind方法各自的功能和特性。
call:call() 方法调用一个函数, 其具有一个指定的 this 值和分别地提供的参数(参数的列表)。
- 注意:该方法的作用和 apply() 方法类似,只有一个区别,就是 call() 方法接受的是若干 个参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。
- 语法: fun.call(thisArg,arg1, arg2, arg3, ...)
- thisArg 在 fun 函数运行时指定的 this 值 如果指定了 null 或者 undefined 则内部 this 指向 window
- arg1, arg2, ... 指定的参数列表 apply:apply() 方法调用一个函数, 第一个参数是一个指定的 this 值,第二个参数是以一个数组 (或类似数组的对象)形式提供的参数。
- 注意:该方法的作用和 call() 方法类似,只有一个区别,就是 call() 方法接受的是若干个 参数的列表,而 apply() 方法接受的是一个包含多个参数的数组。
- 语法: fun.apply(thisArg, [argsArray]) bind:bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数) 具有相同的函数体(在 ECMAScript 5 规范中内置的 call 属性)。
- 当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被 调用时,bind() 也接受预设的参数提供给原函数。
- 一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
- 语法:fun.bind(thisArg,arg1, arg2, arg3, ...)
9. JavaScript中callee和caller有何作用。
Callee:是arguments的一个属性,指向调用arguments的函数自身。多用来处理伪数组。 Caller: 函数对象的一个属性,指向调用当前函数的函数,常见在递归匿名函数。
10. 什么是闭包?闭包的特点是什么。
闭包:一个函数和对其周围状态的引用捆绑在一起,这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函 数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创 建的同时被创建出来 闭包的特点:
- 函数嵌套函数
- 可以在函数外部读取函数内部成员
- 让函数内成员始终存活在内存中
11. let、const、var定义变量的区别是什么
- let 是块级作用域,函数内部使用let 定义后,对函数外部无影响,如果不初始化输出的话,会报语法错误
- const 是块级范围,const 声明的变量,不可以直接修改,也不能被重新声明,必须初始化
- var 全局变量,声明的变量可以修改,如果不初始化输出的话,会报undefined,但不会报错
12. 箭头函数与普通函数的区别是什么
- 语法更简洁
- 不绑定this,会捕获其所在的上下文this值,作为自己的this值
- 是匿名函数,不能作为构造函数
- 箭头函数没有原型属性
- 箭头函数不具备super
13. set和数组的区别是什么?set中如何增加、删除元素
set和数组的区别:不接受 相同的数据的定义和赋值,相同的数据,在SET数据类型中只会存储一次
const s = new Set()
//添加元素,相同元素只会存储一次
s.add(1).add(2).add(3).add(4).add(2)
console.log(s)
//delete删除某个元素,clear清空元素
// console.log(s.delete(100))
// console.log(s)
// s.clear()
// console.log(s)
14. ES6中如何实现类的继承
// 定义父类
class Person {
constructor (name, age) {
this.name = name;
this.age = age;
}
sayHi () {
console.log(`hi,my name is ${this.name}`)
}
}
//子类继承父类
class Student extends Person {
constructor (name,age,number) {
//接收父类的属性
super(name,age)
this.number = number
}
hello () {
//接收父类的方法
super.sayHi()
console.log(`学号是 ${this.number}`)
}
}
const s1 = new Student("tom",18,101)
s1.hello();
15. 类中的static关键字有什么作用
调用静态方法
- 静态方法调用同一个类中的其他静态方法,可使用 this 关键字。
- 非静态方法中,不能直接使用 this 关键字来访问静态方法。而是要用类名来调用: CLASSNAME.STATIC_METHOD_NAME() ,或者用构造函数的属性来调用该方法: this.constructor.STATIC_METHOD_NAME().
16. 类中的静态方法与普通函数的区别?如何定义静态方法
定义静态方法在函数的返回类型前加上关键字static,函数就被定义成为静态函数。
- 其他文件中可以定义相同名字的函数,不会发生冲突
- 静态函数不能被其他文件所用
- 如果静态方法包含this关键字,这个this指的是类,而不是实例
- 静态方法通过类名.方法名调用,不能被实例调用
代码练习
1. 使用解构赋值,实现两个变量的值的交换
let a = "hello";
let b = "world";
[a, b] = [b, a];
// { a: 'world', b: 'hello' }
2. 使用set实现数组去重
function dedupe(array) {
//var newArr = [...new Set(arr)];
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
3. 使用箭头函数改写下列代码
arr.forEach(function (v, i) {
console.log(v, i)
})
改写后:
arr.forEach((v, i) => console.log(v, i));
4. 检测是否是数字,包括整数,小数
function testReg() {
var str = prompt("请输入一个数字");
if (/^-?\d+(\.\d+)?$/.test(str)) {
alert("输入正确,是数字");
} else {
alert("输入错误,非数字");
}
}
5. 检测是否是本地电话,如010-12345678,0418-12345678
var a = prompt("输入本地号码");
var reg1 = new RegExp("0\d{2}-\d{8}|0\d{3}-\d{7}", "g");
if (reg1.test(a)) {
alert("true");
} else {
alert("false");
}
6. 将用户输入的网址全部转化成我们的官网。如www.baidu.com=>www.lagou.com
var a = prompt("输入官网");
var reg = new RegExp("(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*", "g");
a.replace(reg, "www.lagou.com");
7. 实现trim(str)方法,过滤字符串首位空白
function trim(str) {
var reg = /^\s+|\s+$/g;
str = str.replace(reg, "");
console.log(str);
}
8. 获取<p class='demo'>hello JavaScript</p>内部文案
var reg = /^<.+>.+<.+>$/;
// console.log(reg.exec("<p class='demo'>hello icketang</p>"));
var str = "<p class='demo'>hello icketang</p>".replace(/^<.+>(.+)<.+>$/, function(match, $1) {
return $1;
})
console.log(str);
9. 将div#demo.demo转化成
var str = ("div#demo.demo");
str.replace(/^(\w+)#(\w+)\.(\w+)$/,function(match,$1,$2,$3){
return "<"+$1+" id=\"" + $2+ "\" class=\""+ $3+ "\"><\/" + $1 +">"
})
console.log(str);
10. 检测是否是2到4位汉字
// 判断是否是2位至4位长的中文
String regex = "^[\u4e00-\u9fa5]{2,4}$"
11. 检测昵称是否是有汉字字母数字下划线组成,6-8位
function testReg5 () {
var str = prompt("请输入一个昵称");
if (str.length <= 8 && str.length >= 6) {
if (/\_/.test(str)&&/\w+/.test(str)&&/[\u4e00-\u9fa5]/.test(str)) {
alert("昵称格式正确");
} else{
alert("昵称格式错误,昵称由汉字字母数字下划线组成");
}
} else{
alert("请输入6-8位字符");
}
}