首页

关于JavaScript获取时间函数及实现倒计时

前端达人

JavaScript数组对象的迭代方法详解

上一篇博客讲到了数组的方法,当然里边比较复杂的就是数组的迭代方法,因为涉及到了回调函数,所以这篇博客我们来详细讲解一下js数组迭代方法的使用。


1.forEach(funcrion(value,index,arr){}):对数组的每一项运行给定函数,这个方法不进行返回,所以一般用于让数组循环执行某方法。

  var arr=[1,2,3,4,5,6];

    arr.forEach(function(val,index,arr){

        console.log(val,index,arr);

    })

    // 其中:

    // value:每一个数组项的值 必填项

    // index:每一个数组项对应的索引

    // arr:当前的数组


注意:forEach()方法不返回值,所以回调函数中使用return会打印出来undefined

2.map(funcrion(value,index,arr){}):对数组的每一项运行给定函数,它将返回执行函数后的结果组成的新数组。

 var aNum2 = [1.2, 1.8, 2.0, 4.3];

    console.log(aNum2.map(Math.floor()));// [1,1,2,4]

    

    var arr=[1,2,3];

    console.log(arr.map(function(val,index){

        return val*3

    }));// 3 6 9

    // 其中:

    // value:每一个数组项的值 必填项

    // index:每一个数组项对应的索引

    // arr:当前的数组

注意:map()方法有返回值,返回值为新的数组,所以可以直接再回调函数中return

3.every(funcrion(value,index,arr){}):对数组的每一项都运行给定函数,进项判断,若对于每项执行函数都返回了true,则其结果为true。

 var arr=[10,20,30];

    console.log(arr.every(function(val){

        return val>20;

    }));// false

    

    console.log(arr.every(function(val){

        return val>0;

    }));// true

    

    // 其中:

    // value:每一个数组项的值 必填项

    // index:每一个数组项对应的索引

    // arr:当前的数组



注意:every()方法所有的数组项都符合判断时返回true,否则返回false。

4.some(funcrion(value,index,arr){}):对数组的每一项都运行给定函数,进行判断,若存在一项符合条件的数组项,则其结果为true。

    var arr=[10,20,30];

    console.log(arr.some(function(val){

        return val>20;

    }));// true

    

    console.log(arr.some(function(val){

        return val>0;

    }));// true

    

    console.log(arr.some(function(val){

        return val<0;

    }));// false

    

    arr.some(function(val){

        console.log(val<0);

    });//fasle false false

    // 其中:

    // value:每一个数组项的值 必填项

    // index:每一个数组项对应的索引

    // arr:当前的数组


注意:some()方法如果回调函数执行完会根据结果返回true或false,但是回调函数中打印判断是,只会作为判断条件的返回值,则会打印多遍。

5.fliter(funcrion(value,index,arr){}):对数组的每一项都运行给定函数,进行过滤,将符合条件的数组项添加到新的数组中,并返回新的数组。

   var aNum=[1,2,3,4];
    console.log(aNum.filter(function (num) {
        return num > 1;
    }));//[2,3,4,]
    aNum.filter(function (num) {
        console.log(num > 1);//true true true
    })

注意:filter()方法对数组项进行过滤,然后将符合条件的数组项添加到一个新的数组并返回,但是如果直接打印这个判断条件,相当于打印的判断条件的结果,只会返回true或者false。

6.ES6中新增的迭代方法

1.find():返回第一个符合传入测试(函数)条件的数组元素。


  var aNum=[10,20,30,40];

    console.log(aNum.find(function (num) {

        return num > 19;

    }));//1

    console.log(aNum.find(function (num) {

        return num < 0;

    }));//undefined



2.findIndex():返回符合传入测试(函数)条件的数组元素索引。


console.log(aNum.findIndex(function (num) { return num > 19; }));//3


3.includes():判断一个数组是否包含一个指定的值。

总结:

forEach()与map()是一对,用于数组遍历执行指定函数,前者不返回数组,后者返回 处理过的新数组。
every()与some()是一对,分别适用于检测数组是否全部满足某条件或者存在满足的数组项,返回true或false。
filter()则是相当于过滤器的存在,过滤掉数组中不符合条件的数据,将符合条件的数组项添加到新数组,并返回。
————————————————
版权声明:本文为CSDN博主「Mr_Han119」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39155611/java/article/details/106294417


JavaScript 对象可以做到的三件事

seo达人

1. 访问内部属性

JavaScript 对象无法以常规方式访问的内部属性。内部属性名由双方括号[[]]包围,在创建对象时可用。


内部属性不能动态地添加到现有对象。


内部属性可以在某些内置 JavaScript 对象中使用,它们存储ECMAScript规范指定的内部状态。


有两种内部属性,一种操作对象的方法,另一种是存储数据的方法。例如:


[[Prototype]] — 对象的原型,可以为null或对象

[[Extensible]] — 表示是否允许在对象中动态添加新的属性

[[PrivateFieldValues]] — 用于管理私有类字段

2. 属性描述符对象

数据属性包含了一个数据值的位置,在这个位置可以读取和写入值。也就是说,数据属性可以通过 对象.属性 访问,就是我么平常接触的用户赋什么值,它们就返回什么,不会做额外的事情。


数据属性有4个描述其行为的特性(为了表示内部值,把属性放在两对方括号中),称为描述符对象。


属性 解释 默认值

[[Configurable]] 能否通过delete删除属性从而重新定义属性;

能否修改属性的特性;

能否把属性修改为访问器属性 true

[[Enumerable]] 能否通过for-in循环返回属性 true

[[Writable]] 能否修改属性的值 true

[[Value]] 包含这个属性的数据值 undefined

value 描述符是属性的数据值,例如,我们有以下对象 :


let foo = {

 a: 1

}

那么,a 的value属性描述符为1。


writable是指该属性的值是否可以更改。 默认值为true,表示属性是可写的。 但是,我们可以通过多种方式将其设置为不可写。


configurable 的意思是可以删除对象的属性还是可以更改其属性描述符。 默认值为true,这意味着它是可配置的。


enumerable 意味着它可以被for ... in循环遍历。 默认值为true,说明能通过for-in循环返回属性


将属性键添加到返回的数组之前,Object.keys方法还检查enumerable 描述符。 但是,Reflect.ownKeys方法不会检查此属性描述符,而是返回所有自己的属性键。


Prototype描述符有其他方法,get和set分别用于获取和设置值。


在创建新对象, 我们可以使用Object.defineProperty方法设置的描述符,如下所示:


let foo = {

 a: 1

}

Object.defineProperty(foo, 'b', {

 value: 2,

 writable: true,

 enumerable: true,

 configurable: true,

});

这样得到foo的新值是{a: 1, b: 2}。


我们还可以使用defineProperty更改现有属性的描述符。 例如:


let foo = {

 a: 1

}

Object.defineProperty(foo, 'a', {

 value: 2,

 writable: false,

 enumerable: true,

 configurable: true,

});

这样当我们尝试给 foo.a 赋值时,如:


foo.a = 2;

如果关闭了严格模式,浏览器将忽略,否则将抛出一个错误,因为我们将 writable 设置为 false, 表示该属性不可写。


我们还可以使用defineProperty将属性转换为getter,如下所示:


'use strict'

let foo = {

 a: 1

}


Object.defineProperty(foo, 'b', {

 get() {

   return 1;

 }

})

当我们这样写的时候:


foo.b = 2;

因为b属性是getter属性,所以当使用严格模式时,我们会得到一个错误:Getter 属性不能重新赋值。


3.无法分配继承的只读属性

继承的只读属性不能再赋值。这是有道理的,因为我们这样设置它,它是继承的,所以它应该传播到继承属性的对象。


我们可以使用Object.create创建一个从原型对象继承属性的对象,如下所示:


const proto = Object.defineProperties({}, {

 a: {

   value: 1,

   writable: false

 }

})


const foo = Object.create(proto)

在上面的代码中,我们将proto.a的 writable 描述符设置为false,因此我们无法为其分配其他值。


如果我们这样写:


foo.a = 2;

在严格模式下,我们会收到错误消息。


总结

我们可以用 JavaScript 对象做很多我们可能不知道的事情。


首先,某些 JavaScript 对象(例如内置浏览器对象)具有内部属性,这些属性由双方括号包围,它们具有内部状态,对象创建无法动态添加。


JavaScript对象属性还具有属性描述符,该属性描述符使我们可以控制其值以及可以设置它们的值,还是可以更改其属性描述符等。


我们可以使用defineProperty更改属性的属性描述符,它还用于添加新属性及其属性描述符。


最后,继承的只读属性保持只读状态,这是有道理的,因为它是从父原型对象继承而来的。

10 个超有用的 JavaScript 技巧

seo达人

方法参数的验证

JavaScript 允许你设置参数的默认值。通过这种方法,可以通过一个巧妙的技巧来验证你的方法参数。


const isRequired = () => { throw new Error('param is required'); };

const print = (num = isRequired()) => { console.log(`printing ${num}`) };

print(2);//printing 2

print()// error

print(null)//printing null

非常整洁,不是吗?


格式化 json 代码

你可能对 JSON.stringify 非常熟悉。但是你是否知道可以用 stringify 进行格式化输出?实际上这很简单。


stringify 方法需要三个输入。 value,replacer 和 space。后两个是可选参数。这就是为什么我们以前没有注意过它们。要对 json 进行缩进,必须使用 space 参数。


console.log(JSON.stringify({name:"John",Age:23},null,'\t'));

>>>

{

"name": "John",

"Age": 23

}

从数组中获取唯一值

要从数组中获取唯一值,我们需要使用 filter 方法来过滤出重复值。但是有了新的 Set 对象,事情就变得非常顺利和容易了。


let uniqueArray = [...new Set([1, 2, 3, 3, 3, "school", "school", 'ball', false, false, true, true])];

>>> [1, 2, 3, "school", "ball", false, true]

从数组中删除虚值(Falsy Value)

在某些情况下,你可能想从数组中删除虚值。虚值是 JavaScript 的 Boolean 上下文中被认定为为 false 的值。 JavaScript 中只有六个虚值,它们是:


undefined

null

NaN

0

"" (空字符串)

false

滤除这些虚值的最简单方法是使用以下函数。


myArray.filter(Boolean);

如果要对数组进行一些修改,然后过滤新数组,可以尝试这样的操作。请记住,原始的 myArray 会保持不变。


myArray

   .map(item => {

       // Do your changes and return the new item

   })

   .filter(Boolean);

合并多个对象

假设我有几个需要合并的对象,那么这是我的首选方法。


const user = {

    name: 'John Ludwig',

    gender: 'Male'

};

const college = {

    primary: 'Mani Primary School',

    secondary: 'Lass Secondary School'

};

const skills = {

   programming: 'Extreme',

   swimming: 'Average',

   sleeping: 'Pro'

};

const summary = {...user, ...college, ...skills};

这三个点在 JavaScript 中也称为展开运算符。你可以在这里学习更多用法。


对数字数组进行排序

JavaScript 数组有内置的 sort 方法。默认情况下 sort 方法把数组元素转换为字符串,并对其进行字典排序。在对数字数组进行排序时,这有可能会导致一些问题。所以下面是解决这类问题的简单解决方案。


[0,10,4,9,123,54,1].sort((a,b) => a-b);

>>> [0, 1, 4, 9, 10, 54, 123]

这里提供了一个将数字数组中的两个元素与 sort 方法进行比较的函数。这个函数可帮助我们接收正确的输出。


Disable Right Click

禁用右键

你可能想要阻止用户在你的网页上单击鼠标右键。


<body oncontextmenu="return false">

   <div></div>

</body>

这段简单的代码将为你的用户禁用右键单击。


使用别名进行解构

解构赋值语法是一种 JavaScript 表达式,可以将数组中的值或对象的值或属性分配给变量。解构赋值能让我们用更简短的语法进行多个变量的赋值。


const object = { number: 10 };


// Grabbing number

const { number } = object;


// Grabbing number and renaming it as otherNumber

const { number: otherNumber } = object;

console.log(otherNumber); //10

获取数组中的最后一项

可以通过对 splice 方法的参数传入负整数,来数获取组末尾的元素。


let array = [0, 1, 2, 3, 4, 5, 6, 7]

console.log(array.slice(-1));

>>>[7]

console.log(array.slice(-2));

>>>[6, 7]

console.log(array.slice(-3));

>>>[5, 6, 7]

等待 Promise 完成

在某些情况下,你可能会需要等待多个 promise 结束。可以用 Promise.all 来并行运行我们的 promise。


const PromiseArray = [

   Promise.resolve(100),

   Promise.reject(null),

   Promise.resolve("Data release"),

   Promise.reject(new Error('Something went wrong'))];


Promise.all(PromiseArray)

 .then(data => console.log('all resolved! here are the resolve values:', data))

 .catch(err => console.log('got rejected! reason:', err))

关于 Promise.all 的主要注意事项是,当一个 Promise 拒绝时,该方法将引发错误。这意味着你的代码不会等到你所有的 promise 都完成。


如果你想等到所有 promise 都完成后,无论它们被拒绝还是被解决,都可以使用 Promise.allSettled。此方法在 ES2020 的最终版本得到支持。


const PromiseArray = [

   Promise.resolve(100),

   Promise.reject(null),

   Promise.resolve("Data release"),

   Promise.reject(new Error('Something went wrong'))];


Promise.allSettled(PromiseArray).then(res =>{

console.log(res);

}).catch(err => console.log(err));


//[

//{status: "fulfilled", value: 100},

//{status: "rejected", reason: null},

//{status: "fulfilled", value: "Data release"},

//{status: "rejected", reason: Error: Something went wrong ...}

//]

即使某些 promise 被拒绝,Promise.allSettled 也会从你所有的 promise 中返回结果。

高性能Javascript读书总结

前端达人

1. 加载和执行

尽量将所有的<script>标签放在</body>标签之前,确保脚本执行前页面已经完成了渲染,避免脚本的下载阻塞其他资源(例如图片)的下载。

合并脚本,减少页面中的<script>标签

使用<script>标签的defer和async属性(两者的区别见这里)

通过Javascript动态创建<script>标签插入文档来下载,其不会影响页面其他进程

2.数据存取

由于作用域链的机制,访问局部变量比访问跨作用域变量更快,因此在函数中若要多次访问跨作用域变量,则可以用局部变量保存。

避免使用with语句,其会延长作用域链

嵌套的对象成员会导致引擎搜索所有对象成员,避免使用嵌套,例如window.location.href

对象的属性和方法在原型链的位置越深,访问的速度也越慢

3.Dom编程

进行大段HTML更新时,推荐使用innerHTML,而不是DOM方法

HTML集合是一个与文档中元素绑定的类数组对象,其长度随着文档中元素的增减而动态变化,因此避免在每次循环中直接读取HTML集合的length,容易导致死循环

使用节点的children属性,而不是childNodes属性,前者访问速度更快,且不包含空白文本和注释节点。

浏览器的渲染过程包括构建DOM树和渲染树,当DOM元素的几何属性变化时,需要重新构造渲染树,这一过程称为“重排”,完成重排后,浏览器会重新绘制受影响的部分到屏幕中,这一过程称为“重绘”。因此应该尽量合并多次对DOM的修改,或者先将元素脱离文档流(display:none、文档片段),应用修改后,再插入文档中。

每次浏览器的重排时都会产生消耗,大多数浏览器会通过队列化修改并批量执行来优化重排过程,可当访问元素offsetTop、scrollTop、clientTop、getComputedStyle等一系列布局属性时,会强制浏览器立即进行重排返回正确的值。因此不要在dom布局信息改变时,访问这些布局属性。

当修改同个元素多个Css属性时,可以使用CssText属性进行一次性修改样式,减少浏览器重排和重绘的次数

当元素发生动画时,可以使用绝对定位使其脱离文档流,动画结束后,再恢复定位。避免动画过程中浏览器反复重排文档流中的元素。

多使用事件委托,减少监听事件

4.算法和流程控制

for循环和while循环性能差不多,除了for-in循环最慢(其要遍历原型链)

循环中要减少对象成员及数组项的查询次数,可以通过倒序循环提高性能

循环次数大于1000时,可运用Duff Devices减少迭代次数

switch比if-else快,但如果具有很多离散值时,可使用数组或对象来构建查找表

递归可能会造成调用栈溢出,可将其改为循环迭代

如果可以,对一些函数的计算结果进行缓存

5.字符串和正则表达式

进行大量字符串的连接时,+和+=效率比数组的join方法要高

当创建了一个正则表达式对象时,浏览器会验证你的表达式,然后将其转化为一个原生代码程序,用户执行匹配工作。当你将其赋值给变量时,可以避免重复执行该步骤。

当正则进入使用状态时,首先要确定目标字符串的起始搜索位置(字符串的起始位置或正则表达式的lastIndex属性),之后正则表达式会逐个检查文本和正则模式,当一个特定的字元匹配失败时,正则表达式会试着回溯到之前尝试匹配的位置,然后尝试其他路径。如果正则表达式所有的可能路径都没有匹配到,其会将起始搜索位置下移一位,重新开始检查。如果字符串的每个字符都经历过检查,没有匹配成功,则宣布彻底失败。

当正则表达式不那么具体时,例如.和[\s\S]等,很可能会出现回溯失控的情况,在js中可以应用预查模拟原子组(?=(pattern))\1来避免不必要的回溯。除此之外,嵌套的量词,例如/(A+A+)+B/在匹配"AAAAAAAA"时可能会造成惊人的回溯,应尽量避免使用嵌套的量词或使用预查模拟原子组消除回溯问题。

将复杂的正则表达式拆分为多个简单的片段、正则以简单、必需的字元开始、减少分支数量|,有助于提高匹配的效率。

6.快速响应的用户界面

  • 单个JavaScript运算操作时间不应该超出100ms,否则可能会阻塞用户操作
  • 如果要执行长时间的运算,可以通过定时器将计算过程分割成多个步骤,使UI可以得到更新,例如
setTimeout(function(){
    process(todo.shift());

    if (todo.length > 0) {
        setTimeout(arguments.callee, 25);
    } else {
        callback();
    }
})




较长时间的计算过程也可以按照代码运行的时间进行分割,每次控制运行的时间,例如

setTimeout(function(){
    let start = +new Date();
    do {
        process(todo.shift());
    } while(todo.length > 0 && (+new Date() - start) < 50)

    if (todo.length > 0) {
        setTimeout(arguments.callee, 25);
    } else {
        callback();
    }
})


  • 高频率重复的定时器数量尽量要少,建议使用一个独立的重复定时器
  • 使用WebWork进行计算

7. AJAX

  • 设置HTTP头部信息进行缓存,例如
Expires: Mon,28 Jul 2018 23:30:30 GMT


  • 对于一些函数的计算结果进行本地缓存

8. 编程实践

  • 避免使用evalFunction进行双重求值
  • 使用Object/Array字面量定义,不要使用构造函数
  • 使用延迟加载消除函数中重复的工作
  • 使用位操作,例如与1进行按位与计算,得到奇偶交替


if (i & 1) {
    className = 'odd';
} else {
    className = 'even';
}   


  • 多使用JS内置的原生方法,例如Math对象等

9.构建和部署

  • 合并、压缩多个js文件
  • 设置HTTP缓存
  • 使用内容分发网络CDN

10.性能分析工具

————————————————
版权声明:本文为CSDN博主「PAT-python-zjw」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zjw_python/java/article/details/105293878

javascript中的this绑定

前端达人

his是一个关键字,表示执行当前函数的对象

  • this永远跟着当前函数走,
  • 永远是一个对象,

  • 永远在函数执行时才能确定。
  • 1. 默认绑定:没有明确被隶属对象执行的函数,this指向window


function fn(){
    console.log(this);              //window
    console.log(typeof this);       //object
}
fn();

- 严格模式下,this指向undefiend

"use strict";
function fn(){
    console.log(this);              //undefined
}
fn();




如果使用 JavaScript 原型实现继承

seo达人

在这篇文章中,我们将讨论原型以及如何在 JS 中使用它们进行继承。我们还将会看到原型方法与基于类的继承有何不同。


继承

继承是编程语言的一个显著特征,随着面向对象编程语言的引入而出现。这些语言大多是基于类的语言。在这里,类就像一个蓝图,对象是它的展现形式。就是说,要创建一个对象,首先我们必须创建一个类,然后我们可以从一个类创建任意数量的对象。


想象一下,我们有一个表示智能手机的类。这个类具有像其他智能手机一样的可以拍照、有GPS定位等功能。下面是使用 c++ 来描述这样的一个类:


class SmartPhone {

 public:

 void captureImages() {}

}


SmartPhone x;

x.captureImages()

我们创建了一个名为SmartPhone的类,它有一个名为capturePictures的方法用来拍照。


如果我们需要一个iPhone类,它可以捕捉图像和一些特殊的功能,比如面部ID扫描。下面是两种可能的解决方案:


1.将捕获图像功能与其他常见的智能手机功能,以及iPhone的特定功能一起重写到一个新类中。但是这种方法需要更多的时间和精力,并且会引入更多的bug。


重用SmartPhone类中的功能,这就是继承的作用,继承也是重用其他类/对象中功能的一种方式。

这里是我们如何从SmartPhone类中继承capturePictures方法,使用 c++ 实现如下:


class Iphone: public SmartPhone {

 public:

 void faceIDScan() {}

}


Iphone x


x.faceIDScan()


x.captureImages()

上面是一个简单的继承示例。 但是,它表明继承可以使我们以某种方式重用代码,从而使所生成的程序更不易出错,并且花费更少的时间进行开发。


以下是关于类的一些重要信息:


继承该功能的类称为子类

被继承的类称为父类

一个类可以同时从多个类中继承

我们可以具有多个继承级别。 例如,类C继承自类B,而类B继承自类A

值得注意的是,类本身并没有做任何事情。在从类创建对象之前,实际上没有完成任何工作。我们将看到它为什么不同于JavaScript。


大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】。


原型是什么?

在 JS 中,所有对象都有一个特殊的内部属性,该属性基本上是对另一个对象的引用。 此引用取决于对象的创建方式。 在 ECMAScript/JavaScript规范中,它表示为[[Prototype]]。


由于[[Prototype]]链接到一个对象,所以该对象有自己的[[Prototype]]引用。这就是建立原型链的方式。


这个[[Prototype]]链是 JS 中继承的构建块。


__proto__ 对象

为了访问对象的[[Prototype]],大多数浏览器都提供__proto__属性。访问方式如下:


obj.__proto__

需要注意的是,这个属性不是 ECMAScript 标准的一部分,它实际上是由浏览器实现的。


获取和设置原型方法

除了__proto__属性外,还有一种访问[[Prototype]]的标准方法:


Object.getPrototypeOf(obj);

对应的有个类似的方法来设置对象的[[Prototype]]:


Object.setPrototypeOf(obj, prototype);

[[Prototype]]和.prototype属性

[[Prototype]] 只不过是一种用来表示物体原型的标准符号。 许多开发人员将其与.prototype属性混淆,这是完全不同的事情,接着我们来研究一下.prototype属性。


在 JS 中,有许多创建对象的方法。一种方法是使用构造函数,像这样使用new关键字来调用它:


function SmartPhone(os) {

 this.os = os

}


let phone = new SmartPhone('Android')

在控制台打印 phone 对象:


{

 os: "IPhone"

 __proto__{

   constructor: ƒ SmartPhone(os)

  __proto__: Object

 }

}

现在,如果我们希望在phone对象上有一些方法,我们可以在函数上使用.prototype属性,如下所示:


SmartPhone.prototype.isAndroid = function () {

 return this.os === 'Android' || 'android'

}

再次创建phone对象时,打印 phone 对象如下:


{

 os: "Android"

 __proto__{

   isAndroid: ƒ()

   constructor: ƒ SmartPhone(os)

  __proto__: Object

 }

}

我们可以在对象的[[Prototype]]中看到isAndroid()方法。


简而言之,.prototype属性基本上就像由给定的构造函数创建的[[Prototype]]对象的蓝图。 在.prototype属性/对象中声明的所有内容都会在对象的[[Prototype]]中弹出。


实上,如果将 SmartPhone.prototype 与phone 的[[Prototype]]进行比较,就会发现它们是相同的:


console.log(Object.getPrototypeOf(phone) === SmartPhone.prototype);

// true

值得注意的是,我们还可以在构造函数中创建方法:


function ObjectA() {

 this.methodA = function () {}

}


let firstObj = new ObjectA()

console.log(firstObj)

这种方法的问题是当我们初始化一个新对象时。所有实例都有自己methodA的副本。相反,当我们在函数的原型上创建它时,对象的所有实例只共享方法的一个副本,显然使用原型的方式效率会过高。


大家都说简历没项目写,我就帮大家找了一个项目,还附赠【搭建教程】。


当我们访问属性时这里发生了什么?

当我们访问一个属性以获取它时,会发生以下情况:


JS 引擎查找对象上的属性,如果找到了该属性,然后返回它。否则,JS 引擎将通过查看[[Prototype]]来检查对象的继承属性,如果找到该属性,则返回它,否则,它会查找 [[Prototype]]的[[Prototype]]。 找到属性或没有[[Prototype]]时,该链结束,这意味着我们已经到达原型链的末端。


当我们设置/创建属性时,JS 总是在对象本身上进行设置。 即使[[Prototype]]链上存在相同的属性,下面是一个例子:


function MyObject() {}

MyObject.prototype.propA = 10; // 在原型上创建属性


let myObject = new MyObject();

console.log(myObject.propA); // [[Prototype]]上的属性

// 10


myObject.propA = 20; // 对象的属性

console.log(myObject.propA);

// 20

在上面的示例中,我们创建了一个构造函数,该函数的[[Prototype]]上具有属性propA。 当我们尝试对其进行读取操作时,会在控制台中看到该值。 但是,当我们尝试在对象本身上设置相同的属性时;JS 使用给定值在对象上创建一个新属性。 现在,如果我们不能直接访问[[Prototype]]上的属性。


值得注意的是,普通对象的[[Prototype]]链的末尾是内置的Object.prototype。 这就是为什么大多数对象共享许多方法(例如toString())的原因。 因为它们实际上是在Object.prototype上定义的。


使用原型继承的各种方法

在 JS 中,无论我们如何创建对象,只有原型继承,但这些方式还有一些区别,来看看:


对象字面量

在JavaScript中创建对象的最简单方法是使用对象字面量:


let obj = {}

如果在浏览器的控制台中打印obj,我们将看到以下内容:


clipboard.png


基本上,所有用文字面量创建的对象都继承了Object.prototype的属性。


需要注意的是__proto__对象引用了创建它的构造函数。 在这种情况下,constructor属性指向Object构造函数。


使用对象构造函数

另一种不太常见的创建对象的方法是使用对象构造函数。JS 提供了一个名为Object的内置构造函数方法来创建对象。


let obj = new Object();

这种方法的结果与对象字面量的方式相同。它从Object.prototype继承属性。因为我们使用Object作为构造函数。


Object.create 方法

使用此辅助方法,我们可以创建一个带有[[Prototype]]的对象,如下所示:


let SmartPhone = {

 captureImages: function() {}

}


let Iphone = Object.create(SmartPhone)


Iphone.captureImages()

这是在 JS 中使用继承的最简单方法之一。猜猜我们如何在没有任何[[Prototype]]引用的情况下创建对象?


构造方法

与 JS 运行时提供的对象构造函数相似。 我们还可以创建自己的构造函数,以创建适合我们需求的对象,如下所示:


function SmartPhone(os) {

 this.os = os;

}


SmartPhone.prototype.isAndroid = function() {

 return this.os === 'Android';

};


SmartPhone.prototype.isIOS = function() {

 return this.os === 'iOS';

};

现在,我们想创建一个iPhone类,它应该有'iOS'作为它 os 属性的值。它还应该有faceIDScan方法。


首先,我们必须创建一个Iphone构造函数,在其中,我们应该调用SmartPhone构造函数,如下所示:


function Iphone() {

  SmartPhone.call(this, 'iOS');

}

这会将Iphone构造函数中的this.os属性设置为’iOS‘。


之所以调用SmartPhone.call方法,是因为我们需要更改 this 值以引用Iphone。 这类似于在面向对象的世界中调用父级的构造函数。


接下来的事情是,我们必须从SmartPhone构造函数继承方法。 我们可以在此处使用Object.create朋友,如下所示:


Iphone.prototype = Object.create(SmartPhone.prototype);

现在,我们可以使用.prototype为Iphone添加方法,如下所示:


Iphone.prototype.faceIDScan = function() {};

最后,我们可以使用Iphone创建一个对象,如下所示:


let x = new Iphone();


// calling inherited method

console.log(x.isIOS()):

// true


ES6 class

使用ES6,整个过程非常简单。 我们可以创建类(它们与C ++或其他任何基于类的语言中的类不同,只是在原型继承之上的语法糖),然后从其他类派生新的类。


下面是我们如何在ES6中创建类:


class SmartPhone {

 constructor(os) {

   this.os = os;

 }

 isAndroid() {

   return this.os === 'Android';

 }

 isIos() {

   return this.os === 'iOS';

 }

};

现在,我们可以创建一个派生自SmartPhone的新类,如下所示:


class Iphone extends SmartPhone {

  constructor() {

    super.call('iOS');

  }

  faceIDScan() {}

}

我们不是调用SmartPhone.call,而是调用super.call。 在内部,JavaScript引擎会自动为我们执行此操作。


最后,我们可以使用Iphone创建一个对象,如下所示


let x = new Iphone();


x.faceIDScan();


// calling inherited method

console.log(x.isIos()):

// true

该ES6示例与先前的构造方法示例相同。 但是阅读和理解起来要干净得多。


原文:https://javascript.info/proto...


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。

JavaScript拖拽效果

前端达人

要实现JavaScript的拖拽效果,首先我们需要知道事件对象几个有关于实现拖拽效果的坐标

获取事件对象 var e = e || window.event;



根据需求需要用到的拖拽效果的坐标



clientX:鼠标点击位置相对于浏览器可视区域的水平偏移量(不会计算水平滚动的距离)



clientY:鼠标点击位置相对于浏览器可视区域的垂直偏移量(不会计算垂直滚动条的距离)



offsetX:鼠标点击位置相对于触发事件对象的水平距离



offsetY:鼠标点击位置相对于触发事件对象的垂直距离



pageX:鼠标点击位置相对于网页左上角的水平偏移量,也就是clientX加 上水平滚动条的距离



pageY:鼠标点击位置相对于网页左上角的垂直平偏移量,也就是clientY加上垂直滚动条的距离



offsetLeft:如果父元素中有定位的元素,那么就返回距离当前元素最近的定位元素边缘的距离



offsetTop:如果父元素中没有定位元素,那么就返回相对于body左边缘距离



获取元素自身大小:offsetWidth和offsetHeight / clientWidth和clientHeight



offsetWidth和clientWidth的区别:就是offsetWidth包含边框,clientWidth不包含边框



实现拖拽需要用到:clientWidth、clientHeight、clientX、clientY、offsetLeft、offsetTop


首先搭建好html结构和css样式


 <div class="wrap">
        <div class="cover">

        </div>
    </div>

* {
            margin: 0;
            padding: 0;
        }

        .wrap {
            width: 500px;
            height: 500px;
            border: 1px solid deeppink;
            position: relative;
            margin: 50px auto;
        }

        .cover {
            width: 150px;
            height: 150px;
            background: rgba(200, 7, 99, 0.5);
            display: none;
            position: absolute;
            left: 0;
            top: 0;
            cursor: move;
        }
注意:需要给大盒子和小盒子进行定位:子绝父相
接下来就JavaScript代码

<script>
    var wrap = document.querySelector(".wrap");
    var cover = document.querySelector(".cover");
    wrap.onmouseover = function() {
        cover.style.display = "block";
        wrap.onmousemove = function(e) {
            var e = e || window.event;
            var x1 = e.clientX;
            var y1 = e.clientY;
//这里获取到的e.clientX和e.clientY,可以看情况和需求改为e.pageX和e.pageY             
            var halfWidth = cover.clientWidth / 2;
            var halfHeight = cover.clientHeight / 2;
            var wrapLeft = wrap.offsetLeft;
            var wrapTop = wrap.offsetTop;
            var l = x1 - wrapLeft - halfWidth;
            var t = y1 - wrapTop - halfHeight;

            if (l <= 0) {
                l = 0
            }
            if (l >= wrap.clientWidth - cover.clientWidth) {
                l = wrap.clientWidth - cover.clientWidth
            }
            if (t <= 0) {
                t = 0
            }
            if (t >= wrap.clientHeight - cover.clientHeight) {
                t = wrap.clientHeight - cover.clientHeight
            }
            cover.style.left = l + "px";
            cover.style.top = t + "px"
        }
    }
    wrap.onmouseout = function() {
        cover.style.display = "none";
    }
</script>

  var halfWidth = cover.clientWidth / 2;
            var halfHeight = cover.clientHeight / 2;
            var wrapLeft = wrap.offsetLeft;
            var wrapTop = wrap.offsetTop;
            var l = x1 - wrapLeft - halfWidth;
            var t = y1 - wrapTop - halfHeight;
            //限制范围
             if (l <= 0) {
                l = 0
            }
            if (l >= wrap.clientWidth - cover.clientWidth) {
                l = wrap.clientWidth - cover.clientWidth
            }
            if (t <= 0) {
                t = 0
            }
            if (t >= wrap.clientHeight - cover.clientHeight) {
                t = wrap.clientHeight - cover.clientHeight
            }
注意:这里要限制小盒子在大盒子之间移动的范围,左上角的限制,当小盒子超出范围时,将0赋值给l和t。右下角小盒子移动的范围在大盒子宽度减去小盒子的宽度。
其中为了使鼠标一直处于小盒子(cover)的最中间,需要减去小盒子宽度的一半。
再减去大盒子距离页面左边的边距和上边的边距就可以得到坐标

只要鼠标移入大盒子中,就能直接拖拽小盒子,并且鼠标一直处于小盒子的最中间。这样便完成了简单的拖拽效果。

20200315200118747.png
20200315200118747.png


————————————————
版权声明:本文为CSDN博主「_kaze」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luffy_999/article/details/104884538

数据类型的转化(JavaScript)

前端达人

数据类型的转化(JavaScript)—自动转化和强制转化

这一周,我来分享一下在JavaScript中的数据类型转化。

首先我们要知道在JavaScript中的数据类型有什么?在这里我就不详细介绍了,帮你总结好了。

1.布尔类型-----Boolean---isNaN()
    用来判断一个变量是否为非数字的类型,是数字返回false,不是数字返回true。
 2.数值类型-----Number
    存储时,是按照二进制数值存储,输出时,默认都是按照十进制数值输出。
    在JavaScript中二进制前加0b/0B,八进制前面加0 ,十六进制前面加0x。
    如果需要按照原始进制数值输出,用格式为:
            变量名称.toString(进制) ;
    注意的是:S必须大写,将数值转化为字符串形式输出
    如:console.log( a.toString(2) );将a转换成2进制的形式输出。
 3.字符串类型-----String
    JavaScript可以用单引号嵌套双引号, 或者用双引号嵌套单引号(外双内单,外单内双)
    字符串是由若干字符组成的,这些字符的数量就是字符串的长度。
    通过字符串的length属性可以获取整个字符串的长度。
        例子:var str = 'my name is xiaoming';
                  console.log(str.length);
          输出的结果是19。可以知道空格也代表一个字符。
 4.undefined
    表示没有数值-----应该有数值,但是现在没有数值
 5.null
    表示数值为空-----表示有数值,但是数值是“空”
上面就是数据类型的五种形式。那么它是如何转化呢?听我详细给你讲解。

在 JavaScript 程序中 , 变量中存储的数据类型没有限制,也就是在变量中可以存储任何符合JavaScript语法规范的数据类型。但是在 JavaScript 程序的执行过程中,往往需要变量中存储的数值是某种特定的数据类型,别的数据类型不行,此时就需要进行数据类型的转化。
————————————————
版权声明:本文为CSDN博主「什什么都绘」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39406353/article/details/104864224上面就是数据类型的五种形式。那么它是如何转化呢?听我详细给你讲解。

在 JavaScript 程序中 , 变量中存储的数据类型没有限制,也就是在变量中可以存储任何符合JavaScript语法规范的数据类型。但是在 JavaScript 程序的执行过程中,往往需要变量中存储的数值是某种特定的数据类型,别的数据类型不行,此时就需要进行数据类型的转化。
JavaScript中数据类型的转化,分为自动转化和强制转化:
        自动转化是计算机程序,自动完成的转化。
        强制转化是程序员,强行完成的转化
1.布尔类型的自动转化:
 在 执行 if 判断时 ,其他数据类型会自动转化为布尔类型
         其他类型转化为布尔类型的原则
   0   ''   undefined   null  NaN  这五种情况转化为false
          特别提醒 0.0  0.00000  都算是0 
 其他的所有都会转化为 true
2.字符串的自动转化:
  执行字符串拼接, +号的两侧,应该都是字符串类型,会将其他数据类型转化为字符串类型
        转化原则:
            //基本数据类型 / 简单数据类型------将数据数值直接转化为字符串 , 然后执行拼接操作
         布尔值 true  ---> 字符串 'true'
         布尔值 false ---> 字符串 'fasle'
         undefined ---> 字符串 'undefined'
         unll ---> 字符串 'null'
         数值 ---> 将数值解析转化为'对应的纯数字的字符串'
            // 引用数据类型 / 复杂数据类型
         数组 ---> 将 [] 中的内容,转化为字符串的形式,执行拼接操作
         对象 ---> 任何对象,任何内容,都会转化为 [object Object] 固定的内容形式,执行拼接操作
         函数 ---> 将所有的程序代码,转化为字符串,执行拼接操作
3.数值的自动转化:
在执行运算时,会触发数据类型的自动转化。
 转化原则:
    布尔类型 : true  --->  1         
               false --->  0
    undefined : 转化为 NaN 
    null : 转化为 0
    字符串 : 
        如果整个字符串,是纯数字字符串,或者符合科学计数法 ---> 转化为对应的数值
       如果字符串内有不符合数字规范的内容 ---> 转化为 NaN 
    数组,对象,函数:
      如果是+加号执行的是字符串拼接效果,按照这些数据类型转化为字符串的原则来转化
      如果是其他形式的运算 执行结果都是 NaN
4.布尔类型的强制转化:
 
  布尔类型的强制转化就是使用JavaScript中定义好的 方法/函数 Boolean( 数据/变量 )
  Boolean() 这个方法 不会改变 变量中存储的原始数值
   转化原则与自动转化原则相同
     0   ''   undefined  null  NaN --------> false 
     其他数据,都转化为true
5.字符串类型的强制转化:
  
 方法1,变量.toString(进制类型)
         将数值强制转化为字符串,并且可以设定转化的进制,.toString() 之前,不能直接写数值,必须是写成变量的形式
         进制常用的数值是 2 8 16 ,可以设定的范围是 2 - 36 进制  
 方法2,String( 变量 / 数据 )
         将变量或者数据,转化为字符串,原则按照自动转化的原则来执行,不会改变变量中存储的原始数值
         但是在字符串拼接时,会将其他数据类型自动转化为字符串
 6.数字类型的强制转化:
 
方法1 , Number(变量/数值) 
         console.log( Number(true) );   // 1
         console.log( Number(false) );  // 0
         console.log( Number(null) );   // 0
         console.log( Number(undefined) );   // NaN
         console.log( Number('100') );       // 对应的数值
         console.log( Number('100.123') );   // 对应的数值
         console.log( Number('2e4') );       // 对应的数值
         console.log( Number('123abc') );    // NaN
         console.log( Number( [1,2,3,4,5] ) );                           // NaN
         console.log( Number( {name:'zhangsan'} ) );                     // NaN
         console.log( Number( function fun(){console.log('abc')} ) );    // NaN
 将其他类型强制转化为数值类型,转化原则与自动转化选择相同

 方法2, parseInt(变量 / 数据)   是获取变量或者数据的整数部分
         从数据的 左侧起 解析获取 整数内容 
         console.log( parseInt(true) );                 // 都是 NaN            
         console.log( parseInt(false) );                                   
         console.log( parseInt(null) );                                    
         console.log( parseInt(undefined) );                               
         console.log( parseInt( {name:'zhangsan'} ) );                     
         console.log( parseInt( function fun(){console.log('abc')} ) ); 

         数组执行,是获取 数值部分 也就是 没有 []的部分
         1,2,3,4,5  整数部分是 1  1之后是逗号 逗号不是整数,之后的部分也就不算整数
         获取第一个数值的整数部分,如果有就获取,如果没有,结果是NaN
         console.log( parseInt( [1,2,3,4,5] ) );        // 结果是 1                      
         console.log( parseInt( [null,2,3,4,5] ) );     // 结果是 NaN 

         如果是整数就直接获取,如果是浮点数,或者科学计数法,就获取整数部分
         console.log( parseInt( 100 ) );          // 整数是直接获取
         console.log( parseInt( 0.0123 ) );       // 浮点数是获取整数部分
         console.log( parseInt( 3.123456e3 ) );   // 科学计数法是解析之后,获取整数部分

         字符串不同了
         如果是纯数字的字符串
         console.log( parseInt( '100' ) );         // 与数字的结果相同 
         console.log( parseInt( '0.0123' ) );      // 与数字的结果相同 

         console.log( parseInt( '3.123456e3' ) );   //3
         console.log( parseInt( '3abc' ) );   //3
         console.log( parseInt( '3.123' ) );   //3

 方法3 , parseFloat( 变量 / 数值 )
         获取浮点数部分
         console.log( parseFloat(true) );           // 都是 NaN            
         console.log( parseFloat(false) );                                   
         console.log( parseFloat(null) );                                    
         console.log( parseFloat(undefined) );                               
         console.log( parseFloat( {name:'zhangsan'} ) );                     
         console.log( parseFloat( function fun(){console.log('abc')} ) );         
        //数值, 整数,浮点数,都会完整获取
         console.log( parseFloat(100) );            //100
         console.log( parseFloat(100.1234) );       //100.1234
         console.log( parseFloat(1.234567e3) );     //1234.567

         // 关键是字符串
         // 从字符串的左侧起 解析 符合浮点数的部分
         console.log( parseFloat( '100' ) );         // 与数字的结果相同 
         console.log( parseFloat( '0.0123' ) );      // 与数字的结果相同 

         console.log( parseFloat( '3.123456e3' ) );  // 科学技术法会解析
         console.log( parseFloat( '3.1223abc' ) );        
         console.log( parseFloat( '3.123' ) );  
好了,这就是在JavaScript中数据类型的转化,希望可以帮助到你。
————————————————
版权声明:本文为CSDN博主「什什么都绘」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39406353/article/details/104864224

javascript设计模式九:中介者模式

前端达人

中介者对象践行了最少知识原则,指一个对象尽可能少的了解别的对象,从而尽量减少对象间耦合程度。这样各个对象只需关注自身实现逻辑,对象间的交互关系交由中介者对象来实现和维护。



需求背景:



手机购买页面,在购买流程中,可以选择手机的颜色及输入购买数量,同时页面有两个展示区域,分别向用户展示刚选择好的颜色和数量。还有一个按钮动态显示下一步的操作,我们需要查询该颜色手机对应的库存,如果库存数量少于这次购买的数量,按钮将被禁用并显示库存不足,反之按钮可以点击并显示放入购物车。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>中介者模式 购买商品</title>
</head>
<body>
    选择颜色: 
    <select id="colorSelect">
        <option value="">请选择</option>
        <option value="red">红色</option>
        <option value="blue">蓝色</option>
    </select>

    输入购买数量:
    <input type="text" id="numberInput">

    您选择了颜色:<div id="colorInfo"></div><br>
    您输入了数量:<div id="numberInfo"></div><br>

    <button id="nextBtn" disabled>请选择手机颜色和购买数量</button>

</body>
<script>

// 最初级的写法
var colorSelect = document.getElementById('colorSelect'),
    numberInput = document.getElementById('numberInput'),
    colorInfo = document.getElementById('colorInfo'),
    numberInfo = document.getElementById('numberInfo'),
    nextBtn = document.getElementById('nextBtn');

var goods = {
    'red': 3,
    'blue': 6
}

colorSelect.onchange = function(){
    var color = this.value,
        number = numberInput.value,
        stock = goods[color]

    colorInfo.innerHTML = color;

    if(!color){
        nextBtn.disabled = true;
        nextBtn.innerHTML = '请选择手机颜色';
        return;
    }

    if( ( (number-0) | 0 ) !== number-0 ){      //用户输入的购买数量是否为正整数
        nextBtn.disabled = true;
        nextBtn.innerHTML = '请输入正确的购买数量';
        return;
    }

    if(number > stock){     //当前选择数量大于库存量
        nextBtn.disabled = true;
        nextBtn.innerHTML = '库存不足';
        return;
    }

    nextBtn.disabled = false;
    nextBtn.innerHTML = '放入购物车';
}

numberInput.oninput = function(){
    var color = colorSelect.value,
        number = this.value,
        stock = goods[color]

    colorInfo.innerHTML = color;

    if(!color){
        nextBtn.disabled = true;
        nextBtn.innerHTML = '请选择手机颜色';
        return;
    }

    if( ( (number-0) | 0 ) !== number-0 ){      //用户输入的购买数量是否为正整数
        nextBtn.disabled = true;
        nextBtn.innerHTML = '请输入正确的购买数量';
        return;
    }

    if(number > stock){     //当前选择数量大于库存量
        nextBtn.disabled = true;
        nextBtn.innerHTML = '库存不足';
        return;
    }

    nextBtn.disabled = false;
    nextBtn.innerHTML = '放入购物车';
}

</script>
</html>

在上个示例中,对象间联系高度耦合,只是两个输入框还好,但如果有多个的话,相互联系就非常复杂了,此时就要考虑用到中介者模式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>中介者模式 购买商品</title>
</head>
<body>
    选择颜色: 
    <select id="colorSelect">
        <option value="">请选择</option>
        <option value="red">红色</option>
        <option value="blue">蓝色</option>
    </select>

    选择内存: 
    <select id="memorySelect">
        <option value="">请选择</option>
        <option value="32G">32G</option>
        <option value="16G">16G</option>
    </select>

    输入购买数量:
    <input type="text" id="numberInput">

    您选择了颜色:<div id="colorInfo"></div><br>
    您选择了内存:<div id="memoryInfo"></div><br>
    您输入了数量:<div id="numberInfo"></div><br>

    <button id="nextBtn" disabled>请选择手机颜色、内存和购买数量</button>
</body>
<script>
    var goods = {
        'red|32G': 3,
        'red|16G': 0,
        'blue|32G': 1,
        'blue|16G': 6
    }

    //引入中介者
    var mediator = (function(){
        var colorSelect = document.getElementById('colorSelect'),
            memorySelect = document.getElementById('memorySelect'),
            numberInput = document.getElementById('numberInput'),
            colorInfo = document.getElementById('colorInfo'),
            memoryInfo = document.getElementById('memoryInfo'),
            numberInfo = document.getElementById('numberInfo'),
            nextBtn = document.getElementById('nextBtn');

        return {
            changed: function(obj){
                var color = colorSelect.value,
                    memory = memorySelect.value,
                    number = numberInput.value,
                    stock = goods[color + '|' + memory];

                if(obj == colorSelect){      //如果改变的是选择颜色下拉框
                    colorInfo.innerHTML = color;
                }else if(obj == memorySelect){
                    memoryInfo.innerHTML = memory;
                }else if(obj == numberInput){
                    numberInfo.innerHTML = number;
                }

                if(!color){
                    nextBtn.disabled = true;
                    nextBtn.innerHTML = '请选择手机颜色';
                    return;
                }

                if(!memory){
                    nextBtn.disabled = true;
                    nextBtn.innerHTML = '请选择手机内存';
                    return;
                }

                if(!number){
                    nextBtn.disabled = true;
                    nextBtn.innerHTML = '请填写手机数量';
                    return;
                }

                if( ( (number-0) | 0 ) !== number-0 ){      //用户输入的购买数量是否为正整数
                    nextBtn.disabled = true;
                    nextBtn.innerHTML = '请输入正确的购买数量';
                    return;
                }

                if(number > stock){     //当前选择数量大于库存量
                    nextBtn.disabled = true;
                    nextBtn.innerHTML = '库存不足';
                    return;
                }

                nextBtn.disabled = false;
                nextBtn.innerHTML = '放入购物车';
            }
        }
    })()

    colorSelect.onchange = function(){
        mediator.changed(this)
    }

    memorySelect.onchange = function(){
        mediator.changed(this)
    }

    numberInput.oninput = function(){
        mediator.changed(this)
    }

    //以后如果想要再增加选项,如手机CPU之类的,只需在中介者对象里加上相应配置即可。
</script>
</html>
在实际开发中,还是要注意选择利弊,中介者对象因为包含对象间交互的复杂性,所以维护成本可能也会较高。在实际开发中,最优目的还是要快速完成项目交付,而非过度设计和堆砌模式。有时对象间的耦合也是有必要的,只有当对象间复杂耦合确实已经导致调用与维护难以为继,才考虑用中介者模式。

————————————————
版权声明:本文为CSDN博主「一期一会」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34832846/article/details/85989945

JavaScript中的this/call/apply/bind

前端达人

文章目录

一、this

1.什么是this

2.this 代表什么

3.绑定 this 的方法

4.this的指向

5.改变指向

二、Function.prototype.bind()

三、call/apply

1.定义

2.语法

3.异同

一、this

1.什么是this

this 关键字在大部分语言中都是一个重要的存在,JS中自然不例外,其表达的意义丰富多样甚至有些复杂,深刻理解this是学习JS、面向对象编程非常重要的一环。



2.this 代表什么

this代表函数(方法)执行的上下文环境(上下文,类似与你要了解一篇文章,了解文章的上下文你才能清晰的了解各种关系)。



但在 JavaScript 中 this 不是固定不变的,它会随着执行环境的改变而改变。



1.在方法中,this 表示该方法所属的对象。



2.如果单独使用,this 表示全局对象。



3.在函数中,this 表示全局对象。



4.在函数中,在严格模式下,this 是未定义的(undefined)。



5.在事件中,this 表示接收事件的元素。



6.类似 call() 和 apply() 方法可以将 this 引用到任何对象。



3.绑定 this 的方法

this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。



4.this的指向

1.在一般函数方法中使用 this 指代全局对象

function test(){
    this.x = 1;  //这里this就是window
    console.log(this.x);
  }
  test(); // 1



JS规定,函数中的this,在函数被调用时确定,它指函数当前运行的环境。

2.作为对象方法调用,this 指代上级对象

var x =3;
function test(){
  alert(this.x);
}
var o = {
  x:1,
  m:test 
};
o.m(); // 1



如果函数作为对象的方法时,方法中的 this 指向该对象。

3.作为构造函数调用,this 指代new 出的对象

function test(){
    console.log(this);
  }
  var o = new test();
       test();
//可以看出o代表的不是全局对象

new关键词的作用是调用某个函数并拿到其中的返回值,只是调用过程稍特殊。在上面的代码实例中。test函数被new关键词调用时,内部依次执行了以下步骤:

(1)创建一个空对象。

(2)将这个空对象的原型,指向这个构造函数的prototype。

(3)将空对象的值赋给函数内部的this(this就是个空对象了)。

(4)执行函数体代码,为this这个对象绑定键值对。

(5)返回this,将其作为new关键词调用oop函数的返回值。

所以构造函数中的this,依旧是在构造函数被new关键词调用时确定其指向,指向的是当前被实例化的那个对象。

4.箭头函数中的this
箭头函数是ES6的新特性,最重要的特点是它会捕获其所在上下文的this作为自己的this,或者说,箭头函数本身并没有this,它会沿用外部环境的this。也就是说,箭头函数内部与其外部的this是保持一致的。

this.a=20
var test={
    a:40,
    init:()=>{
        console.log(this.a)
        function go(){
            this.a=60
            console.log(this.a)
        }
        go.prototype.a=50
        return go
    }   
}

var p=test.init()
p()
new (test.init())()
//输出 20 60 60 60

5.改变指向
this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。JavaScript 提供了call、apply、bind这三个方法,来切换/固定this的指向。

bind方法和apply、call稍有不同,bind方法返回一个新函数,以后调用了才会执行,但apply、call会立即执行。

二、Function.prototype.bind()
bind()方法主要就是将函数绑定到某个对象,bind()会创建一个函数,函数体内的this对象的值会被绑定到传入bind()中的第一个参数的值,例如:f.bind(obj),实际上可以理解为obj.f(),这时f函数体内的this自然指向的是obj;

示例:
function f(y, z){
    return this.x + y + z;
}
var m = f.bind({x : 1}, 2);
console.log(m(3));
//6
这里bind方法会把它的第一个实参绑定给f函数体内的this,所以这里的this即指向{x : 1}对象,从第二个参数起,会依次传递给原始函数,这里的第二个参数2,即是f函数的y参数,最后调用m(3)的时候,这里的3便是最后一个参数z了,所以执行结果为1 + 2 + 3 = 6分步处理参数的过程其实是一个典型的函数柯里化的过程(Curry)。

三、call/apply
1.定义
每个函数都包含两个非继承而来的方法:call()方法和apply()方法。

call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。

2.语法
call()

调用一个对象的方法,用另一个对象替换当前对象,可以继承另外一个对象的属性,它的语法是:

Function.call(obj[, param1[, param2[, [,...paramN]]]]);
1
obj:这个对象将代替Function类里this对象
params:一串参数列表

说明:call方法可以用来代替另一个对象调用一个方法,call方法可以将一个函数的对象上下文从初始的上下文改变为obj指定的新对象,如果没有提供obj参数,那么Global对象被用于obj。

apply()

和call()方法一样,只是参数列表不同,语法:

Function.apply(obj[, argArray]);

obj:这个对象将代替Function类里this对象
argArray:这个是数组,它将作为参数传给Function

说明:如果argArray不是一个有效数组或不是arguments对象,那么将导致一个TypeError,如果没有提供argArray和obj任何一个参数,那么Global对象将用作obj。

3.异同
相同点

call()和apply()方法的相同点就是这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。

一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向,看个例子:
function add(a, b) {
    return a + b;
}

function sub(a, b) {
    return a - b;
}

console.log(add.call(sub, 2, 1));//3


日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档