})
console.log(proxy.a) // get a
// 10
我们拦截了一个空对象的 读取get
操作, 当获取其内部的属性是,会输出 get ${prop}
, 并返回 10 ;
let proxy = new Proxy({},{
get : function (target,prop,receiver) {
return receiver;
}
})
console.log(proxy.a) // Proxy{}
console.log(proxy.a === proxy) //true
上述 proxy
对象的 a
属性是由 proxy
对象提供的,所以 receiver
指向 proxy
对象,因此 proxy.a === proxy
返回的是 true
。
要注意,如果要访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同,也就是不能对其进行修改,否则会抛出异常~
let obj = {};
Object.defineProperty(obj, “a”, {
configurable: false,
enumerable: false,
value: 10,
writable: false
});
let proxy = new Proxy(obj,{
get : function (target,prop) {
return 20;
}
})
console.log(proxy.a) // Uncaught TypeError
上述 obj
对象中的 a
属性不可写,不可配置,我们通过 Proxy
创建了一个 proxy
的实例,并拦截了它的 get
操作,当我们输出 proxy.a
时会抛出异常,此时,如果我们将 get
方法的返回值修改跟目标属性的值相同时,也就是 10 , 就可以消除异常~
handler.set(target, property, value, receiver)
用于拦截设置属性值的操作,参数于 get
方法相比,多了一个 value
,即要设置的属性值~
在严格模式下,set
方法需要返回一个布尔值,返回 true
代表此次设置属性成功了,如果返回false
且设置属性操作失败,并且会抛出一个TypeError
。
let proxy = new Proxy({},{
set : function (target,prop,value) {
if( prop === ‘count’ ){
if( typeof value === ‘number’){
console.log(‘success’)
target[prop] = value;
}else{
throw new Error(‘The variable is not an integer’)
}
}
}
})
proxy.count = ‘10’; // The variable is not an integer
proxy.count = 10; // success
上述我们通过修改 set
方法,对 目标对象中的 count
属性赋值做了限制,我们要求 count
属性赋值必须是一个 number
类型的数据,如果不是,就返回一个错误 The variable is not an integer
,我们第一次为 count
赋值字符串 '10'
, 抛出异常,第二次赋值为数字 10
, 打印成功,因此,我们可以用 set
方法来做一些数据校验!
同样,如果目标属性是不可写及不可配置的,则不能改变它的值,即赋值无效,如下:
let obj = {};
Object.defineProperty(obj, “count”, {
configurable: false,
enumerable: false,
value: 10,
writable: false
});
let proxy = new Proxy(obj,{
set : function (target,prop,value) {
target[prop] = 20;
}
})
proxy.count = 20 ;
console.log(proxy.count) // 10
上述 obj
对象中的 count
属性,我们设置它不可被修改,并且默认值,我们给定为 10
,那么即使给其赋值为 20
,结果仍旧没有变化!
handler.apply(target, thisArg, argumentsList)
用于拦截函数的调用,共有三个参数,分别是目标对象(函数)target
,被调用时的上下文对象 thisArg
以及被调用时的参数数组 argumentsList
,该方法可以返回任何值。
target
必须是是一个函数对象,否则将抛出一个TypeError
;
function sum(a, b) {
return a + b;
}
const handler = {
apply: function(target, thisArg, argumentsList) {
console.log(Calculate sum: ${argumentsList}
);
return target(argumentsList[0], argumentsList[1]) * 2;
}
};
let proxy = new Proxy(sum, handler);
console.log(sum(1, 2)); // 3
console.log(proxy(1, 2)); // Calculate sum:1,2
// 6
实际上,apply
还会拦截目标对象的 Function.prototype.apply()
和 Function.prototype.call()
,以及 Reflect.apply()
操作,如下:
console.log(proxy.call(null, 3, 4)); // Calculate sum:3,4
// 14
console.log(Reflect.apply(proxy, null, [5, 6])); // Calculate sum: 5,6
// 22
handler.construct(target, argumentsList, newTarget)
construct
用于拦截 new
操作符,为了使 new
操作符在生成的 Proxy
对象上生效,用于初始化代理的目标对象自身必须具有[[Construct]]
内部方法;它接收三个参数,目标对象 target
,构造函数参数列表 argumentsList
以及最初实例对象时,new
命令作用的构造函数,即下面例子中的 p
。
let p = new Proxy(function() {}, {
construct: function(target, argumentsList, newTarget) {
console.log(newTarget === p ); // true
console.log(‘called: ’ + argumentsList.join(’, ')); // called:1,2
return { value: ( argumentsList[0] + argumentsList[1] )* 10 };
}
});
console.log(new p(1,2).value); // 30
另外,该方法必须返回一个对象,否则会抛出异常!
var p = new Proxy(function() {}, {
construct: function(target, argumentsList, newTarget) {
return 2
}
});
console.log(new p(1,2)); // Uncaught TypeError
handler.has(target,prop)
has
方法可以看作是针对 in
操作的钩子,当我们判断对象是否具有某个属性时,这个方法会生效,典型的操作就是 in
,改方法接收两个参数 目标对象 target
和 要检查的属性 prop
,并返回一个 boolean
值。
let p = new Proxy({}, {
has: function(target, prop) {
if( prop[0] === ‘_’ ) {
console.log(‘it is a private property’)
return false;
}
return true;
}
});
console.log(‘a’ in p); // true
console.log(‘_a’ in p ) // it is a private property
// false
上述例子中,我们用 has
方法隐藏了属性以下划线_
开头的私有属性,这样在判断时候就会返回 false
,从而不会被 in
运算符发现~
要注意,如果目标对象的某一属性本身不可被配置,则该属性不能够被代理隐藏,如果目标对象为不可扩展对象,则该对象的属性不能够被代理隐藏,否则将会抛出 TypeError
。
let obj = { a : 1 };
Object.preventExtensions(obj); // 让一个对象变的不可扩展,也就是永远不能再添加新的属性
let p = new Proxy(obj, {
has: function(target, prop) {
return false;
}
});
console.log(‘a’ in p); // TypeError is thrown
数据绑定
上面介绍了这么多,也算是对 Proxy
又来一个初步的了解,那么我们就可以利用 Proxy
手动实现一个极其简单数据的双向绑定
主要看功能的实现,所以布局方面我就随手一挥了~
页面结构如下:
主要还是得看逻辑部分:
//获取段落的节点
const paragraph = document.getElementById(‘paragraph’);
//获取输入框节点
const input = document.getElementById(‘input’);
//需要代理的数据对象
const data = {
text: ‘hello world’
}
const handler = {
//监控 data 中的 text 属性变化
set: function (target, prop, value) {
if ( prop === ‘text’ ) {
//更新值
target[prop] = value;
//更新视图
paragraph.innerHTML = value;
input.value = value;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
完整版面试题资料免费分享,只需你点赞支持,动动手指点击此处就可免费领取了。
前端实习面试的套路
回顾项目
往往在面试时,面试官根据你简历中的项目由点及面地展开问答,所以请对你做过的最好的项目进行回顾和反思。回顾你做过的工作和项目中最复杂的部分,反思你是如何完成这个最复杂的部分的。
面试官会重点问你最复杂的部分的实现方法和如何优化。重点要思考如何优化,即使你项目中没有对那部分进行优化,你也应该预先思考有什么优化的方案。如果这部分答好了,会给面试官留下很不错的印象。
重点在于基础知识
这里指的基础知识包括:前端基础知识和学科基础知识。
前端基础知识:html/css/js 的核心知识,其中 js 的核心知识尤为重要。比如执行上下文、变量对象/活动对象(VO/AO)、作用域链、this 指向、原型链等。
学科基础知识:数据结构、计算机网络、算法等知识。你可能会想前端不需要算法,那你可能就错了,在大公司面试,面试官同样会看重学生这些学科基础知识。
你可能发现了我没有提到React/Vue
这些框架的知识,这里得说一说,大公司不会过度的关注这方面框架的知识,他们往往更加考察学生的基础。
这里我的建议是,如果你至少使用或掌握其中一门框架,那是最好的,可以去刷刷相关框架的面试题,这样在面试过程中即使被问到了,也可以回答个 7788。如果你没有使用过框架,那也不需要太担心,把重点放在基础知识和学科基础知识之上,有其余精力的话可以去看看主流框架的核心思想。
顾和反思。回顾你做过的工作和项目中最复杂的部分,反思你是如何完成这个最复杂的部分的。
面试官会重点问你最复杂的部分的实现方法和如何优化。重点要思考如何优化,即使你项目中没有对那部分进行优化,你也应该预先思考有什么优化的方案。如果这部分答好了,会给面试官留下很不错的印象。
重点在于基础知识
这里指的基础知识包括:前端基础知识和学科基础知识。
前端基础知识:html/css/js 的核心知识,其中 js 的核心知识尤为重要。比如执行上下文、变量对象/活动对象(VO/AO)、作用域链、this 指向、原型链等。
学科基础知识:数据结构、计算机网络、算法等知识。你可能会想前端不需要算法,那你可能就错了,在大公司面试,面试官同样会看重学生这些学科基础知识。
你可能发现了我没有提到React/Vue
这些框架的知识,这里得说一说,大公司不会过度的关注这方面框架的知识,他们往往更加考察学生的基础。
这里我的建议是,如果你至少使用或掌握其中一门框架,那是最好的,可以去刷刷相关框架的面试题,这样在面试过程中即使被问到了,也可以回答个 7788。如果你没有使用过框架,那也不需要太担心,把重点放在基础知识和学科基础知识之上,有其余精力的话可以去看看主流框架的核心思想。