一、面向对象继承笔试题
1.1 判断是自有还是共有
<script>
// 判断自有还是共有
var zs = {
name : '张三',
age : 18
}
zs.__proto__.hobby = '吃饭'
console.log(zs)
console.log(zs.hasOwnProperty('name'))//true
if (zs.hasOwnProperty('hobby')) {
console.log('自有');
} else {
if ('hobby' in zs) {
console.log('共有')
} else {
console.log('没有')
}
}
</script>
1.2 删除和修改
- 自有:
- 修改:obj.属性名=新属性值;
- 删除:delete obj.属性名;
- 共有:
- 修改:原型对象.属性名=新属性值;
- 删除:delete 原型对象.属性名;
<script>
var zs = {
name : '张三',
age : 18
}
zs.__proto__.hobby = '吃饭'
console.log(zs)
// 修改自有
zs.age = 20
console.log(zs)
// 修改共有
zs.__proto__.hobby = '睡觉'
console.log(zs)
// 删除自有
delete zs.age
console.log(zs)
// 删除共有
delete zs.__proto__.hobby
console.log(zs)
</script>
1.3 为老IE的数组添加indexOf方法(如何为一类人创建某个方法)
<script>
// 为老IE的数组创建indexOf方法
if (Array.prototype.indexOf === undefined) {//老IE
Array.prototype.indexOf = function (key,starti) {
starti === undefined && (starti = 0)
for (var i = starti; i < this.length; i++) {
if (this[i] === key) {
return i
}
}
return -1
}
}
var arr = [1,2,34,6,8,5,2]
console.log(arr.indexOf(2))//1
console.log(arr.indexOf(2,2))//6
console.log(arr.indexOf(3))//-1
</script>
<script>
// 为老IE的字符串创建trim方法
if (String.prototype.trim === undefined) {//老IE
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g,'')
}
}
var str = ' wo '
console.log(str.trim());//wo
</script>
<script>
//为数组添加一个求和的方法,以及添加一个求平均值的方法
Array.prototype.add = function () {
for(var i = 0,sum = 0; i < this.length; i++) {
sum += this[i]
}
return sum
}
Array.prototype.avg = function () {
return this.add()/this.length
}
var arr1 = [1,2,3,4,5]
var arr2 = [6,7,8,9,10]
console.log(arr1.add());//15
console.log(arr2.add());//40
console.log(arr1.avg());//3
console.log(arr2.avg());//8
</script>
1.4 如何判断x是不是一个数组
不能用typeof(),typeof()只能检查原始类型,不能检查引用类型,如果检查引用类型得到的结果都是一个object。
<script>
// 判断x是不是数组
var obj={"name":"啦啦啦"}
var arr=[1,2,3,4]
var now=new Date()
var reg=/\s/
// 1、判断x是否继承自Array.prototype
console.log(Array.prototype.isPrototypeOf(obj))//false
console.log(Array.prototype.isPrototypeOf(arr))//true
console.log(Array.prototype.isPrototypeOf(now))//false
console.log(Array.prototype.isPrototypeOf(reg))//false
// 2、判断是不是由Array这个构造函数创建的
console.log(obj instanceof Array)//false
console.log(arr instanceof Array)//true
console.log(now instanceof Array)//false
console.log(reg instanceof Array)//false
// 3、Array.isArray(x); - ES5新增的方法,只有数组可以这么使用
console.log(Array.isArray(obj))//false
console.log(Array.isArray(arr))//true
console.log(Array.isArray(now))//false
console.log(Array.isArray(reg))//false
// 4、输出【对象的字符串】形式
console.log(Object.prototype.toString.call(obj) === '[object Array]')//false
console.log(Object.prototype.toString.call(arr) === '[object Array]')//true
console.log(Object.prototype.toString.call(now) === '[object Array]')//false
console.log(Object.prototype.toString.call(reg) === '[object Array]')//false
</script>
1.5 实现自定义继承:
<script>
// 自定义继承
var zs = {
"phone": "威图",
"dog": "阿拉斯加",
"gf": "很多",
}
var zh = {
"money":10000000
}
zs.__proto__ = zh
console.log(zs);
console.log(zh);
console.log(zs.money);
</script>
<script>
// 批量设置继承
function h5(name,age,hobby) {
this.name = name
this.age = age
this.hobby = hobby
}
var pg = {
js : 200
}
h5.prototype = pg
var zs = new h5('张三',18,'吃饭')
console.log(zs)
</script>
二、class关键字
简化面向对象(封装、继承、多态)
<script>
class lizi {
constructor(price,weight) {
// 里面放的都是自有属性
this.price = price
this.weight = weight
}
// 共有方法
total() {
return `提子的总价为:` + this.price * this.weight
}
}
class fruit extends lizi {
//extends的作用:1、继承自的自有属性全部都不用再写,只需要写一次super(),2、自动继承到共有方法
constructor(price,weight,kind) {
super(price,weight)
this.kind = kind
}
total() {
return `${this.kind}的总价为:` + this.price * this.weight
}
}
var ti = new lizi(11,5)
console.log(ti)
console.log(ti.total());
var fru = new fruit(8,4,'苹果')
console.log(fru)
console.log(fru.total())
</script>
三、闭包
3.1 作用域:
- 全局:随处可用,可以反复使用,缺点:容易被污染
- 函数:只能在函数调用时内部可用,不会被污染,缺点:一次性的,是会自动释放的
3.2 函数的执行原理:
- 程序加载时:
- 创建执行环境栈(ECS):保存函数调用顺序的数组
- 首先压入全局执行环境(全局EC)
- 全局EC引用着全局对象window
- window中保存着我们全局变量
- 定义函数时:
- 创建函数对象:封装代码段
- 在函数对象中有一个scope(作用域)属性:记录着函数来自的作用域是哪里
- 全局函数的scope都是window
- 调用前:
- 在执行环境栈(ECS)压入新的EC(函数的EC)
- 创建出活动对象(AO):保存着本次函数调用时用到的全局变量
- 在函数的EC中有一个scope chain(作用域链)属性引用着AO
- AO有一个parent属性是函数的scope引用着的对象
- 调用时:
- 正是因为有前面三步,才来带变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
- 调用完:
- 函数的EC会出栈,没人引用AO,AO自动释放,局部变量也就释放了
3.3 闭包:
希望保护一个可以【反复使用的局部变量】的一种词法结构,其实还是一个函数,只是写法比较特殊
- 何时使用:希望保护一个可以【反复使用的局部变量】的时候
- 如何使用:
- 两个函数进行嵌套
- 外层函数创建出受保护的变量
- 外层函数return出内层函数
- 内层函数再操作受保护的变量
- 判断是不是闭包:有没有两个函数嵌套,返回了内层函数,内层函数再操作受保护的变量
- 外层函数调用了几次,就创建了几个闭包,受保护的变量就有了几个副本
- 同一次外层函数调用,返回的内层函数,都是在操作同一个受保护的变量
- 缺点:受保护的变量,永远都不会被释放,使用过多,会导致内存泄漏
<script>
function factory() {
var i = 0
return function () {
i ++
return i
}
}
var icbc = factory()
console.log(icbc())//1
console.log(icbc())//2
console.log(icbc())//3
var abc = factory()
console.log(abc())//1
</script>
四、防抖节流
- elem.onmousemove - 鼠标移动事件
<script>
div.onmousemove = function () {
inner()
}
function fdjl() {
var timer = null
return function() {
if (timer !== null) {
clearTimeout(timer)
timer = null
}
timer = setTimeout( ()=> {
div.innerHTML ++
},1000)
}
}
var inner = fdjl()
</script>
- input.oninput - 每次输入/改变都会触发
<script>
inp.oninput = function () {
inner()
}
function fdjl() {
var timer = null
return function () {
if (timer !== null) {
clearTimeout(timer)
timer = null
}
timer = setTimeout( () => {
console.log(inp.value)
},1000)
}
}
var inner = fdjl()
</script>
- onresize - 每次窗口改变大小都会触发
<script>
window.onresize = function () {
inner()
}
function fdjl() {
var timer = null
return function() {
if (timer !== null) {
clearTimeout(timer)
timer = null
}
timer = setTimeout( ()=> {
if (innerWidth > 1280) {
div.style.backgroundColor = '#666'
} else if (innerWidth > 648) {
div.style.backgroundColor = '#fc1'
} else {
div.style.backgroundColor = 'skyblue'
}
},1000)
}
}
var inner = fdjl()
</script>
五、两链一包
5.1 作用域链
以函数的EC的scope chain属性为起点,经过AO,逐级引用,形成的一条链式结构,我们就称之为叫做作用域链
- 作用:查找变量,带来了变量的使用规则:优先使用自己的,自己没有找全局,全局没有就报错
5.2 原型链
每个对象都有一个属性叫做.proto,可以一层一层的找到每个对象的原型对象,最顶层的就是Object的原型,形成的一条链式结构,我们就称之为叫做原型链
- 作用:查找属性和方法
5.3 闭包
希望保护一个可以【反复使用的局部变量】的一种词法结构
- 作用:专门用于防抖节流