一.
什么是函数柯里化?把接受多个参数的函数,转换成一个接受单一参数的函数
就是函数里return 另一个函数
function sum(x){
return function(y){
return x+y
}
}
sum(1)(2) //3
普通方法:
function sum(x,y){
return x+y
}
也就是说,原来实现两个数相加,要在一个括号里传两个参数。
利用函数柯里化,每次调用只传一个参数,返回一个匿名函数,再传一个参数调用,就会得到最后的结果。
三个数相加也同理:
function sum(x){
return function(y){
return function(z){
return x+y+z
}
}
}
console.log(sum(1)(2)(3)) //6
二. Object.defineProperty
它有哪些可配置的选项?
首先,该方法可用于给一个对象添加新的属性,或者,修改已有的属性。
语法:Object.defineProperty(obj, prop, descriptor)
obj:要定义属性的对象
prop:要添加或修改的属性名(一定要加引号!)
descriptor:对象,属性描述符(包括数据描述符和存取描述符两种,只能选其一)
数据描述符:
可以有value writable两个可配置的选项,是一个具有值的属性,该值可以是可写的,也可以是不可写的。
Object.defineProperty(obj,'a',{
value:1,
writable:true
})
value就是该属性的值。
writable 表示该属性是否可写。如果不指定,默认是false,表示不能修改obj.a,但是修改了也不会报错,只是改不了,下次再读a的值,还是1.
存取描述符:
由 getter 函数和 setter 函数所描述的属性
var bValue = 38;
Object.defineProperty(o, "b", {
get() { return bValue; },
set(newValue) { bValue = newValue; },
});
两类描述符可以共有的配置选项
1. enumerable
当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false。
是否是枚举属性,体现在该属性是否能在for(let key in obj)中循环,和Object.keys() 被枚举。如果是ture,这两种操作就能得到该属性。是false,就得不到。
2. configurable
表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。如果不设置,默认就是false。
看看数据描述符:
let obj={}
Object.defineProperty(obj,'a',{
value:1,
enumerable:true,
})
Object.defineProperty(obj,'b',{
value:2,
})
Object.defineProperty(obj,'c',{
value:3,
})
Object.defineProperty(obj,'d',{
value:4,
enumerable:true,
})
for(let key in obj){
console.log(key)
}
console.log(Object.keys(obj))
//结果:
"a"
"d"
["a", "d"]
三. 绑定事件中,@click和@click.native有什么区别
@click
就是给html元素绑定事件
@click.native
是给组件绑定原生事件的,只能用在组件上,不可以用在原生元素上。@click.native 监听组件根元素的原生事件 。
如:父组件A,子组件B
// 父组件:
<template>
<B @click="c"/>
</template>
methods:{
c(){}
}
在子组件的身上监听,如果只写@click,点击B是不会触发事件c的,监听不了。
一种解决方法是在子组件的里边,某一个元素上监听事件@click,如果点击了,触发一个函数a,a里边用$emit手动触发click函数,然后在父组件那里,子组件的身上再用@click监听。
使用.native 修饰符,就可以使代码变得简洁。子组件里边,不再需要监听,触发。只需要在父组件那里,用@click.native ,这样点击B,事件处理函数c能被触发了。
<template>
<B @click.native="c"/>
</template>
@click.self
<div @click.self="c">
aaa
</div>
只有e.target是当前这个元素时,才会触发函数。冒泡或捕获经过它,都不会触发函数。
四. CSS实现各种形状
1. 半圆
.semi{
margin:100px;
width:100px;
height:100px;
background:black;
border-radius:100px 100px 0 0;
height:50px;
}
先画宽高相等的正方形,然后加圆角,左上和右上的圆角值等于宽高值,左下和右下无圆角。半圆:再把高度减半。
2. 扇形
1. 正方形=》加一个圆角
.shan1{
margin:50px;
width:50px;
height:50px;
background:black;
border-radius:50px 0 0 0;
}
圆角值和宽高值相等。四分之一扇形。
2. 给三角形加圆角实现扇形
.shan{
width:0;
height:0;
border-top:50px solid red;
border-left:50px solid transparent;
border-right:50px solid transparent;
border-top-left-radius:50%;
border-top-right-radius:50%;
}
宽高为0,利用border弄出一个三角形。再给三角形加圆角。
3. 三角形
1. 宽高为0,加border
.shan{
width:0;
height:0;
border-top:50px solid red;
border-left:50px solid transparent;
border-right:50px solid transparent;
}
锐角:上边界>左右边界border-top:100px solid red;
border-top:20px solid red;
2. 用clip-path 描点画边
.shan{
background:black;
width:100px;
height:100px;
clip-path:polygon(0px 0px,100px 0px,50px 100px);
}
4. 弧形
.semi{
margin:100px;
width:100px;
height:100px;
background:black;
border-radius:100px 0 100px 0;
transform:rotate(45deg);
}
先正方形,对角加圆角,而且值是宽高值。比如,左上角 和右下角
五. 手写实现call,apply,bind
1. 实现call
let obj={
name:'胡安琪',
say(){
console.log(`我叫${this.name}`)
return this.name
}
}
let obj2={name:'啊啊啊'}
let result=obj.say.call(obj2)
首先明确,调用call的一定是一个函数,不管是对象里的函数还是普通函数。
call()函数需要哪些参数?情况1:可能调用的时候没传参数f() === f.call()
情况2:传了新的thisobj.say.call(obj2) 情况3:除了this,还传了其他参数列表obj.say.call(obj2,1,2,3)
综上,定义一个自己写的myCall函数。它要定义在函数实例的原型上:Function.prototype.myCall=function(context){} 并且接受一个context作为第一个参数,context就是我们指定的调用了call的那个函数里边的新this。如obj2
首先判断,如果没传context,或者传的是undefined 或 null,那么就要修改为window。
怎么把say函数里的this修改为obj2呢? 给obj2也就是context扩展一个属性say,让context也拥有say这个方法,然后调用context.say,这样say函数里的this自然就是context了。具体做法:让context里的一个新属性指向函数say。在myCall函数里的this就是外边调用它的say函数。say.myCall() === say.myCall.call(say) say是函数,函数也是对象,对象.函数的调用就是隐式传this,this就是前边的对象也就是say
Function.prototype.myCall=function(context){
console.log(context)
if(context===undefined || context===null){context=window}
let key=Symbol()
console.log(this)
context[key]=this
let args=[...arguments].slice(1)
let result=context[key](...args)
delete context[key]
return result
}
Symbol() 会生成一个独一无二的symbol值,把这个key变量作为context的一个属性,为了避免与其他属性冲突。
arguments是伪数组,是在调用myCall时包含了context的所有参数。所以要把context排除,得到其余的参数。然后调用这个在context身上扩展的方法。以防say函数里有返回值,我们也要拿到返回值并且return 出去。return 之前还要记得把这个扩展的属性删掉。
重点:context[key]=this给context扩展一个新的属性,指向调用了myCall的那个函数。那个函数在myCall里就是它的this
2. 实现apply
apply和call几乎完全一样,只有参数形式不同。fn.apply(obj2,[1,2,3]) 调用apply,第一个参数是新this,第二个参数必须是数组,数组里的每一项是fn的参数。
所以实现apply,只有处理参数的地方有所不同。
Function.prototype.myApply=function(context){
if(context===undefined || context===null){context=window}
let key=Symbol()
let result
context[key]=this
let args=arguments[1] //数组
if(args){
result=context[key](...args)
}else{
result=context[key]()
}
delete context[key]
return result
}
通过arguments[1] 拿到数组参数,要判断有没有。如果fn.myApply() 时没给传参数列表数组,那args就是undefined。
3. 实现bind
bind的基础用法:
var name='周杰伦'
let obj={
name:'胡安琪',
say(x,y){
console.log(`我叫${this.name},和为${x+y}`)
return this.name
}
}
let obj2={name:'啊啊啊'}
let newsay=obj.say.bind(obj2,3,4)(1,2) //"我叫啊啊啊,和为7"
let newsay=obj.say.bind(obj2,3)(2,1) //"我叫啊啊啊,和为5"
一个函数调用了bind,并且返回一个新函数。调用bind可以传参数,第一个参数是为返回的新函数绑定的this上下文,也就是说,如果调用这个新函数,新函数里的this就是bind后的第一个参数。后边也可以给传一些预设参数。
然后这个新函数拥有say函数相同的功能。调用新函数的时候,也可以给传参数。但是如果say只需要两个实参,但是bind的时候传了两个,调用新函数的时候又传了两个,那么实际只有前两个起作用。如果bind传了一个,新函数传了两个,那么实际也是前两个起作用。
所以两次的参数都需要提取出来。
Function.prototype.myBind=function(context){
if(typeof this !=='function'){return}
let args=[...arguments].slice(1) // [] 或[x,x,x]
let self=this
return function(){
let innerArgs=[...arguments]
return self.apply(context,args.concat(innerArgs))
}
}
返回的新函数,其实就是执行bind前边的那个函数,只不过this是指定的context,其余参数是bind 和新函数的参数拼接,至于真正需要几个,自己就取几个。
bind 返回的新函数,除了正常调用之外,也有可能被当作构造函数 前边加new 调用。当用new调用时,self.apply调用旧函数,这时后边的第一个参数上下文就不是bind绑定的那个context了,而是调用新函数得到的那个实例,let a=new say1() 也就是匿名函数里的this。所以加一个判断,看看这个返回的新函数有没有被new 调用