0728 NOTE
1.setter和getter
- getter 是一种获得属性值的方法,setter是一种设置属性值的方法
- getter负责查询值,它不带任何参数,setter则负责设置键值,值是以参数的形式传递,在他的函数体中,一切的return都是无效的
- get/set访问器不是对象的属性,而是属性的特性,特性只有内部才用,因此在javaScript中不能直接访问他们,为了表示特性是内部值用两队中括号括起来表示如[[Value]]
- 对象的属性又可分为对象属性和访问器属性
- set与get相比于属性本身,执行速度较慢,因此需要时再调用
//var obj = {
// a: 1,
// b: function () {
// return 3;
// }
// }
// 属性通过等号赋值,方法不可以,属性具备存储值的能力,方法不能存储值,
// 属性可以获取值,方法可以返回值,属性只能存储一个值,方法可以传入多个参数
// 属性存储值时,仅存储,方法执行虽不能存值,但是可以执行多个语句块
// obj.a=10;
// var a=obj.b(1,2,3);
// console.log(obj.a);
// 处理一个介于属性和方法之间方式,就是为了可以存值的同时,可以执行多个语句块
// var divs=document.querySelectorAll("div");
// var a;
// setNum(2);
// console.log(a);
// function setNum(n){
// a=n;
// for(var i=0;i<divs.length;i++){
// divs[i].textContent=n+i;
// }
// }
// var obj = {
// _a: 0,
// set a(value) {
// this._a = value
// var divs = document.querySelectorAll("div");
// for (var i = 0; i < divs.length; i++) {
// divs[i].textContent = value + i;
// }
// },
// get a() {
// return this._a;
// }
// }
// obj.a=10;
// obj.a=20;
// console.log(obj.a);
// console.log(obj.a);
// obj.a() 不存在
// obj.a=obj.a+2;
// setInterval(function(){
// obj.a++;
// },200)
// 注入式
// 特征:注入数据,改变显示内容,数据驱动显示,一般用于父元素注入数据给子元素,驱动子元素内容显示改变
// var obj = {
// _a: 0,
// // set a(value) {
// // this._a = value
// // var divs = document.querySelectorAll("div");
// // for (var i = 0; i < divs.length; i++) {
// // divs[i].textContent = value + i;
// // }
// // },
// get a() {
// return this._a;
// }
// }
// 仅设置了set,没有设置get,这种情况就是仅能设置,不能获取
// 仅设置了get ,没有设置set,这种情况就是仅能读取,不能设置 ,只读
// console.log(obj.a);
// 写法种类
// 新创建对象设置set和get
// var obj = {
// _a: 0,
// set a(value) {
// this._a = value
// },
// get a() {
// return this._a;
// }
// }
// 针对已有对象
// var div=document.querySelector("div");
// Object.defineProperties(div,{
// _width:{writable:true,value:0},
// _height:{writable:true,value:0},
// _bg:{writable:true,value:"rgba(0,0,0,0)"},
// width:{
// set:function(value){
// this._width=value;
// this.style.width=value+"px";
// },
// get:function(){
// return this._width;
// }
// },
// height:{
// set:function(value){
// this._height=value;
// this.style.height=value+"px";
// },
// get:function(){
// return this._height;
// }
// },
// bg:{
// set:function(value){
// this._bg=value;
// this.style.backgroundColor=value;
// },
// get:function(){
// return this._bg;
// }
// }
// })
// div.width=50;
// div.height=50;
// div.bg="red";
// div.width=1;
// div.height=30;
// div.bg="red";
// setInterval(function(){
// div.width++;
// },16);
// ES6面向对象
// class Box{
// _num=0;
// constructor(){
// }
// set num(value){
// this._num=value;
// }
// get num(){
// return this._num;
// }
// // 常量
// get EVENT_ID(){
// return "Event_ID";
// }
// }
// var b=new Box();
// b.num=10;
// console.log(b.num);
// b.EVENT_ID=12;
// console.log(b.EVENT_ID);
2.闭包
闭包 是指内部函数总是可以访问其所在的外部函数中声明的变量和参数,即使在其外部函数被返回(寿命终结)了之后。在某些编程语言中,这是不可能的,或者应该以特殊的方式编写函数来实现。但是如上所述,在 JavaScript 中,所有函数都是天生闭包的,但是使用 new Function
创建一个函数,那么该函数的[[Environment]]
并不指向当前的词法环境,而是指向全局环境。因此,此类函数无法访问外部(outer)变量,只能访问全局变量。
通常,函数调用完成后,会将词法环境和其中的所有变量从内存中删除。因为现在没有任何对它们的引用了。与 JavaScript 中的任何其他对象一样,词法环境仅在可达时才会被保留在内存中。但是,如果有一个嵌套的函数在函数结束后仍可达,则它将具有引用词法环境的 [[Environment]]
属性。
当词法环境对象变得不可达时,它就会死去(就像其他任何对象一样)。换句话说,它仅在至少有一个嵌套函数引用它时才存在。
闭包特点
-
作用域空间不销毁
- 优点: 因为不销毁,变量页不会销毁,增加了变量的生命周期
- 缺点: 因为不销毁,会一直占用内存,多了以后就会导致内存溢出
-
可以利用闭包访问再一个函数外部访问函数内部的变量
- 优点: 可以再函数外部访问内部数据
- 缺点: 必须要时刻保持引用,导致函数执行栈不被销毁
-
保护私有变量
- 优点: 可以把一些变量放在函数里面,不会污染全局
- 缺点: 要利用闭包函数才能访问,不是很方便
// function fn(){ // var a=1; // return function(){ // a++; // console.log(a); // } // } // var f=fn(); // f(); // var utils=(function(){ // var a=1; // return { // init:function(){ // a++; // console.log(c); // } // } // })(); // utils.init(); // var div=document.querySelector("div"); // div.οnmοusedοwn=function(e){ // document.οnmοusemοve=function(e1){ // div.style.left=e1.clientX-e.offsetX+"px"; // div.style.top=e1.clientY-e.offsetY+"px"; // } // document.οnmοuseleave=function(){ // document.οnmοusemοve=null; // document.οnmοuseleave=null; // document.οnmοuseup=null; // } // document.οnmοuseup=function(){ // document.οnmοusemοve=null; // document.οnmοuseleave=null; // document.οnmοuseup=null; // } // } // 会造成内存泄漏,所有的闭包都会造成内存泄漏 // 优点 会产生私有变量,减少变量污染 // function bind(fn,obj){ // return function(){ // fn.apply(obj,arguments) // } // } // function fns(_a,_b){ // this.a=_a; // this.b=_b; // console.log(this.a+this.b); // } // var obj={a:1,b:2}; // var f=bind(fns,obj); // f(3,5);
3.函数柯里化
柯里化(Currying)是一种关于函数的高阶技术。它不仅被用于 JavaScript,还被用于其他编程语言。
柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c)
转换为可调用的 f(a)(b)(c)
。
柯里化不会调用函数。它只是对函数进行转换。
// function curry(){
// var arr=[];
// return function(){
// arr=[].concat(arguments);
// console.log(arr);
// console.dir(arguments);
// return arr;
// arr=[].concat.apply(arr,arguments);
// if(arguments.length>0){
// [].push.apply(arr,arguments)
// }else{
// return arr.reduce((value,item)=>value+item);
// }
// }
// }
// function getSum(){
// }
// getSum(1);
// getSum(2,3);
// getSum(4,5,6);
// getSum(7,8,9,10);
// var sum=getSum();
// console.log(sum);
// var fn=curry();
// fn(1,2,3)
// fn(4,5,6)
// var s=fn();
// console.log(s);
// function currying(fn){
// var arr=[];
// return function (){
// if(arguments.length>0){
// [].push.apply(arr,arguments);
// return arguments.callee;
// }else{
// // return fn.apply(null,arr);
// return fn(...arr);
// }
// }
// }
// var f=currying(function(){
// return [].reduce.call(arguments,(value,item)=>value+item);
// return [].reduce.apply(arguments,[(value,item)=>value+item])
// })
// var f=currying((...arg)=>{
// return arg.reduce((value,item)=>value+item);
// })
// f(1,2,3);
// f(4,5,6);
// f(8,9);
// var s=f();
// console.log(s);
// var s=f(3,4,5)(2,1)(6,7)();
// console.log(s);
// function fn(a,b,c,d){
// var s=[].reduce.call(arguments,function(value,item){
// return value+item;
// },0);
// console.log(s);
// }
// fn(1,2,3,4);
4.原型
函数有原型,对象有原型链
-
在 JavaScript 中,对象有一个特殊的隐藏属性
[[Prototype]]
(如规范中所命名的),它要么为null
,要么就是对另一个对象的引用。该对象被称为“原型”。 -
当从
object
中读取一个缺失的属性时,JavaScript 会自动从原型中获取该属性。在编程中,这种行为被称为“原型继承”。 -
__proto__
是[[Prototype]]
的因历史原因而留下来的 getter/setter -
__proto__
属性有点过时了。它的存在是出于历史的原因,现代编程语言建议我们应该使用函数Object.getPrototypeOf/Object.setPrototypeOf
来取代__proto__
去 get/set 原型。-
__proto__
被认为是过时且不推荐使用的(deprecated),这里的不推荐使用是指 JavaScript 规范中规定,proto 必须仅在浏览器环境下才能得到支持。现代的方法有:
- [Object.create(proto, descriptors]) —— 利用给定的
proto
作为[[Prototype]]
和可选的属性描述来创建一个空对象。 - Object.getPrototypeOf(obj) —— 返回对象
obj
的[[Prototype]]
。 - Object.setPrototypeOf(obj, proto) —— 将对象
obj
的[[Prototype]]
设置为proto
。
应该使用这些方法来代替
__proto__
。 - [Object.create(proto, descriptors]) —— 利用给定的
-
-
原型仅用于读取属性。对于写入/删除操作可以直接在对象上进行。
-
无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,
this
始终是点符号.
前面的对象。 -
for..in
循环也会迭代继承的属性。(如果想排除继承的属性,那么通过内建方法 obj.hasOwnProperty(key):如果obj
具有自己的非继承的 属性,则返回true
。) -
按照规范,所有的内建原型顶端都是
Object.prototype
。这就是为什么有人说“一切都从对象继承而来”。 -
如果
F.prototype
是一个对象,那么new
操作符会使用它为新对象设置[[Prototype]]
。(注意,这里的F.prototype
指的是F
的一个名为"prototype"
的常规属性。每个函数都有"prototype"
属性,默认属性) -
默认的
"prototype"
是一个只有属性constructor
的对象,属性constructor
指向函数自身。 -
默认情况下,所有函数都有
F.prototype = {constructor:F}
,所以可以通过访问它的"constructor"
属性来获取一个对象的构造器。
// function fn(){
// }
// 通常直接执行函数,原型没有什么作用,当函数作为构造函数使用时,原型就有意义了
// fn.prototype
// fn();
// fn.call();
// fn.apply();
// new 函数这种写法,叫做实例化,被new的函数叫做构造函数,js中构造函数和类名相同
// var a=new fn();
// console.log(a);
// ES5
// function Box(n){
// // console.log(n);
// }
// var b=new Box(5);
// console.log(b.constructor===Box);
// ES6
// class Box{
// constructor(n){
// console.log(n);
// }
// }
// var c=new Box(5);
// console.log(c.constructor===Box);
// ES5
// Box就是构造函数
function Box(){
// console.log(this);
}
// console.log(Box.prototype);
// Box.prototype.a=1;
// Box.prototype.b=function(){
// console.log("aa");
// }
// Box.prototype={
// constructor:Box,
// a:1,
// b:function(){
// }
// }
// Object.assign(Box.prototype,{
// a:1,
// b:function(){
// console.log("aaa");
// }
// })
// Box();
// 实例化构造函数
// 构造函数的原型被执行实例化时放入了实例化对象的原型链中
// var b=new Box();
// console.log(b.__proto__===Box.prototype);
// console.log(b);
// ES6
class Box1{
a=0;
static b=10;
constructor(a){
this.a=a;
}
play(){
console.log("aaaa");
}
static run(){
console.log("bbb");
}
}
// Box.b
// Box.run()
var a=new Box1(5);
console.log(a);
console.log(Box1.b);
Box1.run();
// ES5
// function Box(a){
// this.a=a;
// }
// Box.prototype.a=0;
// Box.prototype.play=function(){
// console.log("aaaa");
// }
// Object.defineProperty(Box.prototype,"play",{
// value:function play(){
// console.log("aaaa");
// }
// })
// Box.b=10;
// Box.run=function(){
// console.log("bbb");
// }
// Box.run();
// console.log(Box.b);
// var b=new Box(5);
// console.log(b);
// console.log(Box.b);
// Box.run()
Array.prototype.a=function(){
// console.log("aaa");
console.log(this);
}
var arr=[1,2,3];
arr.a();
5.原型重构
Array.prototype.slice_1 = function (start, end) {
if (start === undefined) start = 0;
if (end === undefined) end = this.length;
if (start < 0) start = this.length + start;
if (start < 0) start = 0;
if (start > this.length) start = this.length;
if (end < 0) end = this.length + end;
if (end < 0) end = 0;
if (end > this.length) end = this.length;
var arr1 = [];
for (var i = start; i < end; i++) {
arr1[arr1.length] = this[i];
}
return arr1;
}
// var arr=[1,2,3,4,5,6];
// var arr1=arr.slice_1(3,5);
// console.log(arr1);
// Array.prototype.reduce_1=function(fn,initValue,thisArg){
// if(this.length===0) throw new Error("Reduce of empty array with no initial value");
// var index=0;
// if(initValue===undefined){
// initValue=this[0];
// index=1;
// }
// for(;index<this.length;index++){
// if(index in this) initValue=fn.call(thisArg,initValue,this[index],index,this);
// }
// return initValue;
// }
// var arr=[1,2,3];
// var obj={};
// var s= arr.reduce_1(function(value,item){
// value+=item
// this[item]=value;
// return value
// },100,obj)
// console.log(s,obj);
// arr.forEach()
// arr.map()
// arr.some()
// arr.every()
// arr.filter()
// arr.flatMap()
// var arr1=arr.map(function(item){
// this[item]=item+10;
// return item+10;
// },obj);
// console.log(arr1,obj);
// div.rect(50,50,"red");
// HTMLElement.prototype.rect=function(w,h,bg){
// Object.assign(this.style,{
// width:isNaN(w) ? w : w+"px",
// height:isNaN(h) ? h : h+"px",
// backgroundColor:bg
// })
// }
// var div=document.querySelector("div");
// div.rect(50,50,"Red")
// var p=document.querySelector("p");
// p.rect(100,100,"orange")
// function fn(){
// }
// var f=fn.bind({a:1});
Function.prototype.bind_1=function(thisArg){
// this 因为是原型方法,所以this被实例化的对象,谁调用bind_1,this就是谁
// console.log(this);
var f=this;
return function (){
// this 因为回调函数执行,所以是window
// console.log(this);
f.apply(thisArg,arguments);
}
}
// function fns(a,b){
// this.a=a;
// this.b=b;
// this.s=a+b;
// }
// var o={}
// var f=fns.bind_1(o)
// f(3,5);
// console.log(o);
// var obj={
// init:function(){
// document.addEventListener("click",this.clickhandler.bind_1(this));
// },
// clickhandler:function(e){
// console.log(this);
// }
// }
// obj.init()
// function fn1(f){
// f();
// }
// function fn2(){
// }
// fn1(fn2);
// Function.prototype.curry=function(thisArg=null){
// var f=this;
// var arr=[];
// return function(){
// if(arguments.length>0){
// [].push.apply(arr,arguments);
// return arguments.callee;
// }else{
// return f.apply(thisArg,arr);
// }
// }
// }
// function getSum(...arg){
// return arg.reduce((value,item)=>{
// if(!this.includes(item)) value+=item;
// return value;
// });
// }
// var arr=[3,5,7]
// var f=getSum.curry(arr);
// f(1,2,3);
// f(4,5);
// var s=f();
// console.log(s);
// Object.defineProperties(Array.prototype,{
// length1:{
// set:function(v){
// v=Number(v);
// if(isNaN(v)) throw new Error("Error Length,must is Number");
// Object.defineProperty(this,"_length1",{value:v,writable:true})
// if(v<this.length){
// for(;v<this.length;v++){
// this.pop();
// }
// }else{
// for(var len=v-this.length,i=0;i<len;i++){
// this.push(undefined);
// }
// }
// },
// get:function(){
// return this._length1;
// }
// }
// })
// var arr=[1,2,3,4];
// arr.length1=10;
// console.log(arr);
// var div=document.querySelector("div");
// console.dir(div);
6.proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
const p = new Proxy(target, handler)
/*
target
要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
*/
术语
包含捕捉器(trap)的占位符对象,可译为处理器对象。
traps
提供属性访问的方法。这类似于操作系统中捕获器的概念。
target
被 Proxy 代理虚拟化的对象。它常被作为代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。
在不改变原对象的基础上实现对该对象的扩充
// var obj={a:21}
// var obj1=new Proxy(obj,{
// get:function(target,p){
// console.log(target,p);
// return target[p]+10;
// },
// set:function(target,p,value){
// target[p]=value;
// }
// })
// console.log(obj1.a);
// set
// var div=document.querySelector("div");
// var o={width:0,height:0,backgroundColor:"red"};
// var o1=new Proxy(o,{
// set:function(target,p,value){
// target[p]=value;
// if(p==="width" || p==="height") value=isNaN(value) ? value : value+"px";
// div.style[p]=value;
// }
// })
// o1.width=50;
// o1.height=50;
// o1.backgroundColor="red";
// console.log(o);
// var o={}
// var p=new Proxy(o,{
// set:function(target,prop,value){
// if(typeof value!=="number"){
// throw new Error("value must is Number");
// }
// if(!Number.isInteger(value)){
// throw new Error("value must is Integer");
// }
// target[prop]=value;
// }
// })
// p.a=3.5
// console.log(p);
// construct
// function Box(a,b,c,d){
// console.log(a,b,c,d);
// }
// var Ball=new Proxy(Box,{
// construct:function(target,argArr,newTarget){
// console.log(target,argArr,newTarget);
// return new target(...argArr,10);
// }
// })
// new Ball(1,2,3);
// new Box(1,2,3)
// has
// var o1={abc:10};
// var obj={a:1,b:2,c:3,d:4,e:undefined};
// Object.setPrototypeOf(obj,o1);
// console.log(obj);
// var o=new Proxy(obj,{
// has:function(target,p){
// return target.hasOwnProperty(p) && (p in target)
// }
// })
// console.log("a" in o);
// apply
function fn(a,b){
console.log(a,b,this);
}
var div=document.querySelector("div");
var f=new Proxy(fn,{
apply:function(target,thisArg,argArr){
// console.log(target,thisArg,argArr);
target.apply(div,argArr);
}
})
// f.apply(null,[1,2,3]);
// f(1,2,3)
// f.call(null,1,2,3)
// f(3,5)
// f.call({a:1},3,5)
// f.apply({a:1},[3,5])
f(3,5);
f.call({a:1},3,5)
var arr=[1,2,3];
var arr1=arr.slice(0,2);
// var divs=document.querySelectorAll("div");
// var divlist=[].slice.call(divs,0,2);
// var slice=[].slice.uncurry();
// var divslist=slice(divs,0,2)