1. 使用call,apply来实现继承
```
// call(obj, arg1, arg2):将参数1为调用对象的上下文,使调用的方法当做自己的方法来使用。
// 相同点:这两个方法的作用是一样的,都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域
//一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向
// 不同点:接收参数的方式不同。apply(obj, [arg1, arg2]) call(obj, arg1, arg2)
function Pet (words) {
this.words = words;
this.speak = function (){
console.log(this.words)
}
}
function Dog (words) {
// 将当前的this即Dog指向Pet,此时Dog拥有了Pet中的所有属性和方法。
Pet.apply(this, arguments)
// Pet.call(this, words)
}
var dog = new Dog('wang')
dog.speak();
```
2. js类型
> 原始类型(5种):boolean, string, number, null, undefined
引用类型(6种):Date, Array, Error, Function, object, regexp
引用类型分为function和object,使用typeof测试时返回类型也是这两个,function是一种对象,它也可以有自己的属性和方法,function的功能要强于object。
3. function与object的区别?
>1. function可以被执行
>2. function可以被当做object的构造函数,当我们使用new操作符后面跟着一个function类型的变量时,这个function变量会被当成构造函数,返回一个object对象。
>如:function Foo() ; var foo = new Foo()
>3. function有内置的prototype属性,而object没有,因为function可当做构造函数。
4. _proto_(隐式原型)与prototype(显示原型)的区别
> prototype仅函数有此属性,而_proto_是函数和对象都有此属性
> 一个对象的隐式原型指向构造该对象的构造函数的原型
function Foo(){}
var Boo = {name: “Boo”};
Foo.prototype = Boo;
var f = new Foo();
console.log(f.proto === Foo.prototype); // true
console.log(f.proto === Boo); // true
Object.getPrototypeOf(f) === f.proto; // true
5.
```
var a = 100;
function foo() {
console.log(a);
a = 10;
console.log(a);
console.log(this.a);
var a; //---->(1)
}
// 结果为:undefined 10 100
// (1)处改为 function a() {},结果为:function a方法, 10, 100
// (1)处改为var a = function() {}, 结果为:undefined, 10, 100
```
6. 删除数组的某个元素
```
// 方案一
// splice(index,len,[item]) 该方法会改变原始数组。
// index:下标 len: 替换/删除的长度 item:替换的值,删除操作 item为空,
Arrary.prototype.removeByValue = function(val) {
for (var i = 0; i < this.length; i++) {
if (this[i] === val) {
this.splice(i, 1)
break;
}
}
}
// 方案二(ES6)
arr.splice(arr.findIndex(item => item.name === val), 1)
// 方案三 (filter)
arr = arr.filter(({ name }) => name !== val);
```
7. 实现千分位
```
// 方案一(正则表达式)
// ?=表示正向引用,可以作为匹配的条件,但匹配到的内容不获取,并且作为下一次查询的开始
// $& 表示与正则表达式相匹配的内容,具体的可查看 [w3school的replace()方法](https://www.w3school.com.cn/jsref/jsref_replace.asp)
function format (num) {
var reg=/\d{1,3}(?=(\d{3})+$)/g;
return (num + '').replace(reg, '$&,');
}
```

```
// 方案二
function format(num){
num=num+'';//数字转字符串
var str="";//字符串累加
for(var i=num.length- 1,j=1;i>=0;i--,j++){
if(j%3==0 && i!=0){
str+=num[i]+",";//加千分位逗号
continue;
}
str+=num[i];//倒着累加数字
}
return str.split('').reverse().join("");//字符串=>数组=>反转=>字符串
}
// 方案三
// unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度
// shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
// push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
// pop() 方法用于删除并返回数组的最后一个元素。
// slice(start, end) 方法可从已有的数组中返回选定的元素。
function format(num) {
var arr = [],
str = num + '',
count = str.length;
while (count >= 3) {
arr.unshift(str.slice(count - 3, count));
count -= 3;
}
// 如果是不是3的倍数就另外追加到上去
str.length % 3 && arr.unshift(str.slice(0, str.length % 3));
return arr.toString();
}
// 方案四reduce
function format(num) {
var str = num+'';
// ["8", "7", "6", "5", "4", "3", "2", "1"]
return str.split("").reverse().reduce((prev, next, index) => {
return ((index % 3) ? next : (next + ',')) + prev;
})
}
console.log(format(12345678));
```
8. 运算符
> 算术运算符:+, -, *, /, ++, - -,%
比较运算符:>, <,\==\=,!==,>=,<=
逻辑运算符:!,&&,||
条件运算符:表达式 ?值1 :值2
9. 分支结构
> switch(值|表达式){val:语句;break; default: 语句}
while(条件){语句;} // 直到型循环
do{语句}while(条件)
break: 结束整段程序; continue:结束本次循环。
10. $与window.onload的区别?
> 1. \$比onload运行速度快
> 文档加载分为两步:1. 内存加载DOM对象; 2. Dom对象显示。使用window.onload是在内存加载完DOM对象,并且DOM对象完全显示后才触发。而$是在内存加载完DOM对象后就立即触发,故速度比较快。
> 2.可以完全可靠的绑定一个事件的多个操作。
\$(function() {alert('a')}) \$(function() {alert('b')})
11. <script>代码段放在<head>与<body>下的区别?
> 这与执行机制有关,放在head中时系统会先解释(预编译)再加载body,加载完body后直接执行。而放在body中,系统先加载body,加载完后还要对script解释,然后再执行,速度会慢。
12. 页面加载的顺序
先加载<head>之间的内容,然后加载<body>之间的内容,直接在<head>之间书写jquery代码,浏览器会立即执行脚本,但是由于页面元素在body部分才出现,此时尚未加载,所以jqurey操作无法应用到页面元素上。 解决方法:将代码放在$(function(){代码}),函数等到页面加载完毕后执行,相当于onload事件。
13. event.preventDefault() // 阻止事件的默认动作
14. event.stopPropagation() // 阻止事件冒泡
15. jquery链式写法的原理:每个方法返回的都是前面调用它的对象,即return this
16. JS的嵌入方式?
> 1. 内嵌方式:\<script type='text/javascript'>代码\</script>
> 2. 外联方式:(1)首先将js代码保存独立的js文件
(2)\<script type='text/javascript' src='js文件' />
17. 闭包?
> 指有权访问另一个函数作用域中的变量的函数,创建闭包常见的方式,就是在一个函数内部创建另一个内部(私有)函数。
用途:1.可以读取函数内部的变量; 2. 让这些变量的值始终保持在内存中。
闭包-->返回函数的函数 --->实现封装
特点: 实现对象的封装,提供统一的方法来访问对象里的属性。
```javascript
function test() {
var x = 10;
return function() {
return x;
}
}
alert(x) // 调用局部变量x,报错未定义。
var a = test()
alert(a())
// a实际上就是闭包匿名函数
// 函数test中的局部变量x一直保存在内存中,并没有在test调用后被自动清除。
```
18. 怎么理解基于对象?
> 1. js是由大量对象构成(JS的所有东西都可视为对象)
>2. 不是面向对象编程语言(无继承、封装、多态等面向对象编程语言的语法),但是因为大型项目会用到面向对象编程语言,所以可以模拟面向对象编程语言的编程思想。
19. 如何理解JS的安全性?
> JS的安全性指的是操作安全,因为JS没有访问操作系统的权限,所以不能操作文件和注册表等系统资源,从而不能用来制造病毒和木马。
20. 变量赋值赋的是值,对象赋值赋的是首地址。
```javascript
var a = [2,3,4]
b = a
b.push(7)
// 因为a与b是同一地址,同一内存空间,故a和b的值都为[2,3,4,7]
// 注:销毁对象用Null, 销毁变量用Undefined。
```
21. 能重写自己的函数,由于一个函数可以返回另一个函数,因此我们可以用新的函数来覆盖旧的函数
```javascript
function a() {
alert('第一次的a函数’)
a = function() {
alert('将会覆盖原a函数')
}
}
a() // 执行外围的a,一般用于一次性的准备工作
a() // 再次执行a,执行的是覆盖后的函数。
``
22. 类与对象的区别(类相当于模型、类型,即构造函数)?
>1. 类中属性无值(或为默认值),对象属性有值
>2. 对象由类实例化
>3. 对象中属性和方法必须出自类
>4. 类不能直接使用
23. 什么是面向对象?
> 使用对象的时候只关注对象提供的功能,而不关注其内部实现的细节。
其特点为:抽象、封装、多态、继承
24. JS如何实现继承?
> 属性使用call继承,方法由原型实现共享继承。
25. JS内置对象有:String、Array、Math、Date、Number、Function、RegExp、Error
- String对象的方法
charAt(n): 返回字符串中的第n个字符,从0开始计算,若n不合法则返回该字符串。
charCodeAt(n): 返回的是字符编码,若找不到返回NaN
concat('字符串‘):字符串连接
fromCharCode(unicode): 使用unicode创建字符串,unicode可为多值,逗号分隔
indexOf(要搜索的字符串,开始搜索的位置): 从开始位置搜索,返回第一个匹配的字符串的位置,未找到返回-1
localeCompare(str): 比较两个字符串,若相等为0,大于返回正数,小于则为负数
match(regexp/g): 返回所匹配的字符串到一个新数组中,若不加/g,则只匹配第一个。
replace(str, str1): 替换第一个匹配的字符串到一个新字符串,若想匹配所有使用/g
search(regexp): 检索与正则表达式相匹配的字符串
slice(start, end): 截取[start, end]间的字符串
split(分隔符,size): 按指定分隔符将字符串转化为限定大小的数组
substr(start, n):从start位置开始截取n个字符串
subString(start, end):截取[start, end]位置的字符串
toLowerCase: 转小写 toUpperCase: 转大写
toString(): 将其他类型的数据转换为字符串
- Array对象的方法
concat(str, str2…): 将参数添加到原数组中,返回新数组,原数组不变
join('分隔符’):将数组按分隔符连接为字符串
push(): 将新元素置于数组尾部
pop(): 删除数组尾部的元素
reverse():将数组反序,原数组不变
shift(): 删除原数组第一个元素
unShift():在数组的头部添加元素
slice(start, end):返回[start,end]之间的所有元素,若无end,则返回start开始的所有元素,start,end也可为负责,-1代表最后一个元素,-2为倒数第二元素,类推
sort(callback): 按字符编码顺序排序,根据callback返回值排序
splice(start,n,str): 从start开始删除n项,并使用str替换删除的n项
- Math对象的方法
floor(x): 对x进行下取整
ceil(x): 对x进行上取整
max(x, y):返回x,y的最大值 ,min(x,y)返回最小值
cfqj random(): 返回0~1的随机数
round(x):把x四舍五入为最接近的整数
abs(x): 返回x的绝对值
26. 编程题
function a(x=y, y=2) {
return [x,y]
}
a() // 报错,y没有定义
var x = 2
var x
console.log(x) // 结果为2,var只会被定义一次
setTimeout(function() {
console.log(1)
},0)
new Promise(function(resolve, reject) {
console.log(2)
resolve()
console.log(3)
}).then(function(){
console.log(4)
})
console.log(5)
// 结果为:2,3,5,4,1
- 实现数组的奇偶数排序
// newSort不可挂在Array.__proto__上,Array.__proto__是一个Function
// Array.__proto__.__proto__ === Array.prototype.__proto__(true,都是object)
Array.prototype.newSort = function() {
// 不可写为this = this.sort(),不可改变this的值
var arr = this.sort()
return arr.filter(item => item%2).concat(arr.filter(item=> !(item%2)))
}
// 上面的方法不会改变原数组,若想改变原数组需要添加
arr.map((item, index) =>this[index] = item)
- 数组扁平化并将其中重复部分数据去除,最终得到一个升序且不重复的数组
Array.from(new Set(a.flat().sort()))
let b = 7;
(function b() {
b = 9;
console.log(b);
})();
// 结果为function b()
// function赋值后置,晚于普通变量赋值
let b
function b()
b = 7
b = 9
function b()
(function(){
var a = b = 3;
}());
console.log(b); // 3
console.log(a); // a is not defined
/*
首先分析 var a = b = 3 可以拆分为
b = 3 b是全局变量,在立即执行函数里赋值为3
var a = b a是立即执行函数里声明的变量,函数执行完a就被释放,
并没有生成全局变量a,所以在全局输出a会报错:a is not defined
*/
// 当使用var声明变量时,i会在全局作用域中找到它的值,此时i为10,结果是输出10次10
for (var i=0; i<10; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
// let和var不同,当使用let声明变量时,是有块级作用域的,let声明的i都会存在于for块级作用域中,每一次for循环都会生成一个块级作用域
// 输出0-9
for (let i=0; i<10; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
for (var i=0; i<10; i++) {
(function(i) {
setTimeout(() => {
console.log(i)
}, 1000)
})(i)
}
// 输出0-9
window.onload = function() {
console.log(1)
};
$(function() {
console.log(2)
});
(function() {
console.log(3)
})();
window.onload = function() {
console.log(4)
};
$(function() {
console.log(5)
});
// 结果:3,2,5,4(window.onload会覆盖,$(function(){})可定义多个
- let、const、var的区别
const定义的变量不可以修改,而且必须初始化
var定义的变量可以修改,如果不初始化会输出undefined,不会报错。
let是块级作用域,函数内部使用let定义后,对函数外部无影响。
- 如何实现一个百度搜索框的功能?怎么做输入提示?
// 利用百度jsonp接口实现搜索提示语.
function jsonp(url, options) {
// 创建script标签
var $script = document.createElement('script');
// 解决缓存问题
var f = url.indexOf('?') > - 1 ? '&' : '?';
url += f + '_=' + Date.now();
// 把参数拼接到url上面
for(var i in options) {
url += '&' + i + '=' + options[i];
}
$script.src = url;
document.body.appendChild($script);
}
// baidu.js:实现具体功能函数
var baiduInput = (function(){
var timer = null;
return {
init: function(ele) {
this.$ele = document.querySelector(ele);
this.$inputSearch = this.$ele.querySelector('input');
this.$listTipsBox = this.$ele.querySelector('.search-list');
this.event();
},
event:function(){
var _this = this;
this.$inputSearch.onfocus = function() {
// 判断文本内是否有文字,如果有就显示下拉框
_this.checkInput();
_this.getData();
}
this.$inputSearch.oninput = function() {
//判断文本内容为空, 隐藏下拉框,如果有文字显示下拉框
_this.checkInput();
clearInterval(timer);
// 目的: 减少http请求, 降低对服务器的压力
timer = setTimeout(function() {
_this.getData();
}, 500)
// 根据输入的内容,获取下拉框数据, 并渲染到下拉框中
},
document.onclick = function(e) {
if(e.target !== _this.$inputSearch) {
// 如果点击的不是搜索框, 让搜索框中的下拉框隐藏
_this.listShow();
}
}
// 利用事件委托给每一个li添加点击事件
this.$listTipsBox.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if(target.nodeName === 'LI') {
// 把li上面的文本赋值给文本框
_this.$inputSearch.value = target.innerHTML;
_this.listShow();
}
}
},
listShow: function(val) {
val = val || 'none';
this.$listTipsBox.style.display = val;
},
checkInput: function(val) {
// 获取文本框的值
val = val || this.$inputSearch.value;
if(val === '') {
this.listShow();
} else {
this.listShow('block');
}
},
getData: function(val) {
if (val === '') return;
val = val || this.$inputSearch.value;
var params = {
wd: val,
cb: "baiduInput.insertData"
}
jsonp('https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su', params);
},
insertData: function(data) {
data = data.s;
data = data.map(function(x) {
return "<li>" + x + "</li>";
})
this.$listTipsBox.innerHTML = data.join('');
}
}
}())
- 函数add同时满足add(a,b)和add(a)(b),且输出结果为a+b,如:add(2,3)和add(2)(3)均得到5
1 function add(a,b){
2 if(typeof b=="undefined"){ //或者arguments.length==0
3 return function(b){
4 return a+b;
5 }
6 }else{
7 return a+b;
8 }
9 }
10 console.log(add(2,3)); //5
11 console.log(add(2)(3)); //5
追问:如果有传入多个参数,怎样实现上述函数。如:add(1)(2)(3)(4)
function add(a){
2 function fun(b){
3 a+=b;
4 return fun;
5 }
6 fun.toString=function(){
7 return a;
8 }
9 return fun;
10 }
11 console.log(add(1)(2)(3)(4)); //10