JavaScript中的call,apply与bind

一.它们有什么作用?

都可以改变this的指向

<script>

let obj = {name: 'Pray', age: 20}
// 这里不能写箭头函数, 因为箭头函数中的this始终指向当前这个函数
// let fun = () => console.log(this)
// 先声明函数
let fun = function (arg) {
console.log(this, arg)
}
// 这里打印的this为Window对象
fun()

// 下面执行函数打印的三个this都为obj中的对象,而不是Window
fun.call(obj)
fun.apply(obj)
fun.bind(obj)() // bind只是将函数返回 ,需要自己去调用
console.log('----------------------')
// 传参的区别 (apply的参数必须是数组, 存放在数组里面, 其它不需要)
fun.call(obj, 'arg')
fun.apply(obj, ['arg'])
fun.bind(obj, 'arg')()

</script>

二 .它们有什么不同 ?

1.bind绑定完this不会立即执行, 而是会将函数返回出来

2.apply传参必须是数组这种格式

三. 应用

修改回调中的this (vue, react 经常使用到!)

let obj = {name: 'Pray', age: 20}
setTimeout(function () {
<!--此时this为obj对象-->
console.log(this)
}.bind(obj),1000)

10月11日补充, 当apply指向null或者 undefined 时

我在网上看到一个关于 apply 指向的代码, 看完代码 , 代码如下

oldParseInt.apply(null, arguments);

当一个 apply 指向 null 时, 代表了什么呢? 我很好奇的问了些朋友讨论了一下 , 其实, 当 apply 指向 null或者undefined 时, 这个this指向代表了全局对象, 可以是Windows 或者 global(nodejs) 。 在严格模式下, 会报错, null 只代表了个值, 没有意义

分享到

JavaScript利用history对象手写个 vue-router

什么是history对象

DOM window 对象通过 history 对象提供了对浏览器历史的访问。它暴露了很多有用的方法和属性,允许你在用户浏览历史中向前和向后跳转,同时——从HTML5开始——提供了对history栈中内容的操作。

譬如平时使用比较多的 ,向前和向后跳转

在history中向后跳转:

window.history.back();

这和用户点击浏览器回退按钮的效果相同。

类似地,你可以向前跳转(如同用户点击了前进按钮):

window.history.forward();

跳转到 history 中指定的一个点

你可以用 go() 方法载入到会话历史中的某一特定页面, 通过与当前页面相对位置来标志 (当前页面的相对位置标志为0).

向后移动一个页面 (等同于调用 back()):

window.history.go(-1);

向前移动一个页面, 等同于调用了 forward():

window.history.go(1);

类似地,你可以传递参数值2并向前移动2个页面,等等。

您可以通过查看长度属性的值来确定的历史堆栈中页面的数量:

var numberOfEntries = window.history.length;

注意: IE 支持传递URLs作为参数给 go(); 这在Gecko是不标准且不支持的。

而前面这些 History 属性, 要实现一个类似 vue-router的功能还是不够的, 在 html5, 新增的history.pushState和history.replaceState 刚好能满足我们的需求

两个api都接受三个参数

  • 状态对象(state object):一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
  • 标题(title):FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。
  • 地址(URL): 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。

相同之处是两个API都会操作浏览器的历史记录,而不会引起页面的刷新。不同之处在于pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录

大家可以先在控制台试试,看看地址栏发生了什么变化

window.history.pushState(null, null, "test");
window.history.pushState(null, null, "/test");
window.history.pushState(null, null, "#/hello");
window.history.pushState(null, null, "?name=");

实现代码

<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="UTF-8">
<title>前端路由实现</title>
<style>
.wapper {
width: 500px;
height: 500px;
border: 1px solid #999999;
margin: 0 auto;
}

.wapper ul {
height: 39px;
border-bottom: 1px solid #999999;
}

.wapper ul li {
padding-left: 12px;
display: inline-block;
list-style: none;
}

.content {
padding: 30px;
}

a {
cursor: pointer;
}
</style>
</head>
<body>
<section class="wapper">
<ul class="header">
<li><a href="javascript:void(0)">首页</a></li>
<li><a href="javascript:void(0)">新闻</a></li>
<li><a href="javascript:void(0)">关于</a></li>
</ul>
<div class="content">
<!-- 内容加载区域 -->
</div>
</section>
<script>
let header = document.querySelector('.header')
let content = document.querySelector('.content')
// 页面初始化
let url = '默认展示内容';
history.replaceState(url, null, '');
content.innerHTML = url
// 点击事件处理
header.removeEventListener('click', _eventCallBackFun)
header.addEventListener('click', _eventCallBackFun)
function _eventCallBackFun(e) {
if (e.target.tagName === 'A')
url = e.target.innerHTML
content.innerHTML = url
history.pushState(url, null, '#/' + url);
}
// 监听 popstate 事件
window.addEventListener('popstate', e => {
url = e.state
content.innerHTML = url
})
</script>
</body>
</html>
分享到

使用JavaScript实现队列的相关入队、出队、获取队头、判空的操作

首先,什么是队列?

队列,又称为伫列(queue),是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。

队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。

来自维基百科

其实,通过上面的话可以知道,队列在JavaScript代表的是数组

队列的基本运算

  • 初始化队列:Init_Queue(q) ,初始条件:队q 不存在。操作结果:构造了一个空队;

  • 入队操作:In_Queue(q,x),初始条件:队q 存在。操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化;

  • 出队操作:Out_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 删除队首元素,并返回其值,队发生变化;

  • 读队头元素:Front_Queue(q,x),初始条件: 队q 存在且非空,操作结果: 读队头元素,并返回其值,队不变;

  • 判队空操作:Empty_Queue(q),初始条件: 队q 存在,操作结果: 若q 为空队则返回为1,否则返回为0

我们今天来用JavaScript实现 队列的相关入队、出队、获取队头、判空一系列操作,复习一遍数组的使用

代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JS队列</title>
</head>
<body>
<input id="queue-input" type="text">
<p id="queue-cont">队列内容:apple-&gt;pear</p>
<button onclick="inQueue()" id="in-btn">入队</button>
<button onclick="outQueue()" id="out-btn">出队</button>

<script>

let el = document.querySelector('#queue-cont')
let Input = document.querySelector('#queue-input')
let queue = ["apple", "pear"];

function inQueue() {
el.innerHTML = In_Queue(queue, Input.value).join('->')
}

function outQueue() {
el.innerHTML = Out_Queue(queue)
}

//入队
function In_Queue(q, x) {
isErr(q)
q.push(x)
return q
}

// 出队
function Out_Queue(q) {
isErr(q)
q.shift()
return q
}

// 打印队头元素内容
function Front_Queue(q) {
isErr(q)
return q.slice(0, 1)
}

// 判断队列是否为空
function Empty_Queue(q) {
if (!q.length) {
return 1
}
return 0
}

// common fun
function isErr(q) {
if (!q) {
throw "q为空";
}
}


</script>
</body>
</html>
分享到

NodeJS连接到MongoDB

mongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 -来自维基百科

我们本地先安装配置mongoDB,然后启动mongoDB服务

启动后默认端口为 27017

image

1.创建数据库(名为mall)

mongo控制台输入 use mall

我们只创建一个数据库的话,默认还是没有这个数据库的, 还需要创建表(集合)

2.创建数据库表

db.createCollection(goods) (创建goods表)

当然我们可以也可以自己导入本地数据库表

mongoimport -d mall -c goods --file /User/db/goods

-d代表database, 既数据库名称。-c 代表数据库集合,代表了goods这张表, 后面 –ff 加文件地址

导入完成后 ,数据库中就有goods这张表和名为mall的数据库了。

3.我们使用nodejs 的第一方框架 express 搭建一个名为mall模板项目。

express mall

4.安装mongoose并设置表数据映射关系

 Mongoose是在node.js异步环境下对mongodb进行便捷操作的对象模型工具  

控制台输入 npm i mongoose --save

在项目中新建一个models文件夹, 创建goods.js文件,用来存放对应的数据关系表

goods.js

//引入mongoose
var mongoose = require('mongoose')

//定义商品表模型
var Schema = mongoose.Schema;
var produtSchema = new Schema({
"productId":{type:String},
"productName":String,
"salePrice":Number,
"checked":String,
"productNum":Number,
"productImage":String
});
// 将Good模型(produtSchema)输出 Good对应了mongoDB中的goods这张表 --- 注意, mongoose.model 第一个参数必须要是mongoDB对应的表,并且要大写, 不能加s , 如下文中的Good
module.exports = mongoose.model('Good',produtSchema);

5.创建表路由,连接到本地的mongoDB服务

var express = require('express');
var router = express.Router();
// 引入mongoose
var mongoose = require('mongoose');
// 引入goods模块
var Goods = require('../models/goods');

//连接MongoDB指定的数据库
mongoose.connect('mongodb://127.0.0.1:27017/mall');

// 连接成功
mongoose.connection.on("connected", function () {
console.log("MongoDB connected success.")
});
// 连接失败
mongoose.connection.on("error", function () {
console.log("MongoDB connected fail.")
});
// 连接断开
mongoose.connection.on("disconnected", function () {
console.log("MongoDB connected disconnected.")
});

router.get("/", function (req, res, next) {

Goods.find({},function (err, doc) {
//请求失败 ,返回错误信息
if (err) {
res.json({
status: '1',
msg: err.message
})
//请求成功
} else {
res.json({
status: '0',
msg: '',
result: {
count: doc.length,
list: doc
}
})
}
})
})

module.exports = router

4. 在app.js中配置goods路由

var goodsRouter = require('./routes/goods');

-----------------
-----------------

app.use('/goods', goodsRouter);

启动该项目,浏览器URL输入 localhost:3000/goods/ 访问到goods路由

如果连接成功并且显示到正确的数据,既表示数据库连接成功并且已经返回数据接口

image

(完)

分享到

使用nodejs加载静态资源

当我们想通过node服务加载一些静态资源怎么做呢?

其实很简单, nodejs提供了诸多的类库给我们使用

loadStaticFile.js

let http = require('http')
//url库
let url = require('url')
//通用工具库
let util = require('util')
//有关文件流处理库
let fs = require('fs')

let server = http.createServer((req, res) => {
//获取url文件路径
const pathName = url.parse(req.url).pathname

//使用util类中的substring方法,清除url前一个无用字符
fs.readFile(pathName.substring(1), (err, data) => {
// 当本地没有这个文件时, 设置404, 反之
if (err) {
res.writeHead(404, {
'Content-Type': 'text/html'
})
} else {
res.writeHead(200, {
'Content-Type': 'text/html'
})
// 写入数据
res.write(data.toString())
}
res.end('ok')
})
})
// 监听服务的端口
server.listen(3000, '127.0.0.1', () => {
console.log('服务启动成功,请打开localhost:3000访问')
})

我们在本地新建一个 index.html 。 启动服务 , 访问

http://127.0.0.1:3000/index.html

你会发现,该url指向的是本地的index.html文件, 并且响应头信息是我们之前设置的200, 当我们通过url访问一个本地不存在的静态文件时,返回的就是404了

(完)

分享到

使用nodejs创建一个服务

首先,我们需要在电脑上需要安装nodejs,并且环境变量配置正确

创建一个service.js文件

service.js

// 引入nodejs中的http库,采用CommonJS规范
let http = require('http')

// 创建服务
let server = http.createServer((req, res) => {

// 设置响应头信息...
res.statusCode = 200
res.setHeader('Token','xxxxToken')
res.setHeader('content-type', 'text/html;charset=utf-8')


//此时一定要写res.end , 如果服务端没有数据传回客户端就可以直接用red.end返回,如果有数据可以使用res.send,red.json此时可以不写res.end了,因为在前面两个方法中默认会返回
res.end('connect over')

})

// 服务设置监听端口
server.listen(3000,'127.0.0.1',()=>{
console.log('服务启动成功,请输入 http://127.0.0.1:3000/ 进行访问')
})

如何启动这个服务?

cmd控制台输入 node service
回车, 显示

服务启动成功,请输入 http://127.0.0.1:3000/ 进行访问

没有报错信息 , 即表示文件运行成功

输入http://127.0.0.1:3000/ , 查看响应头信息, 我们可以看到,我们在service.js中设置的token与content-type已经生效

image

用nodejs 启动一个服务是不是很简单?

(完)

分享到

网页图片加载失败处理方法

问题描述

网页中有时候可能会出现图片资源载入失败的问题,比如 Safari 错误图片载入的样子 , 会显示问号 ,如下图 。

image

这种问题可能还会影响我们后面写的一些样式, 那这种问题怎么处理呢 ?

其实解决这种问题很简单

JavaScript提供了一个 onerror 事件 ,在装载文档或图像的过程中如果发生了错误,就会调用该事件,
它支持的HTML标签有 img object style

我们只需要替换错误的图片就好了,不让浏览器使用默认值 ,代码如下

<img src="xxx" onerror="javascript:this.src='bg.png';" alt="pic" />

衍生问题

当图片加载失败时, 默认图片也加载失败了, 那怎么办呢?

思路是当图片加载失败时,进入 onerror , 判断onerror 的图片是否能加载, 在onerror 中的图片再次触发onerror的时候,设置onerror为null(null表示不存在该对象)

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<script>
function errorIMG(){
//获取img对象,火狐是event.target ,IE及谷歌其他是event.srcElement
var img=event.srcElement || event.target ;
//设置默认图片
img.src = 'bg.png'
img.onerror=null;
}
</script>

<body>

<img src="xxx" onerror="errorIMG()">

</body>

</html>

(完)

分享到

函数中的prototype

prototype(原型)一直都是我非常想了解的东西 , 这东西比较高级,涉及到js的原理, 今天学习了函数中的原型对象, 分享一下

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script>
/**
* 1.函数的prototype属性
* 每个函数都有一个prototype属性,它默认指向一个 object 空对象(即原型对象)
* 原型对象中都有一个 constructor, 它指向的当前对象 !
*
* 2.给原型对象添加属性(一般都是方法)
* 作用: 说明所有实例自动拥有原型对象中的属性方法
*/
window.onload = function () {
function f() {
console.log('fun f')
}

console.log(f.prototype)
// 给函数对象添加方法
f.prototype.addFun = function () {
console.log('addFun()')
}

// 判断constructor是否指向当前对象
console.log(f.prototype.constructor === f) //true
console.log(Date.prototype.constructor === Date) //true


// 我们可以实例化原型
let newFun = new f()
// 调用原型方法
newFun.addFun()
// 打印出addFun()
// 说明函数中的所有实例对象自动拥有原型中的属性方法!
}
</script>


</html>

image

图中的type是一个函数

总结:函数有一个prototype属性 , 指向一个object空对象(即原型对象!!!), 原型对象都拥有 constructor 属性, constructor又指向了当前的对象 (构造函数与原型对象有相互引用的关系)
分享到

let进阶

再一次的学习let,更加深入

/**
 * let关键字的特点
 * 1。不会预处理, 不存在变量提升
 * 2。有自己的作用域,不可以全局或者作用域内重复声明
 */
let关键字的应用: 循环遍历加监听
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
window.onload = function () {
// let aa = 1

console.log(aaaa)
var aaaa = 1
// 变量提升(预处理)
// 打印的是 undefined

console.log(bbbb)
let bbbb = 1
// 变量提升(预处理) 这里出错 使用let , 不存在预处理 !!!! , 打印的是 Uncaught ReferenceError: bbbb is not defined
// at window.onload

let aa = 0
console.log(aa)

function aaa() {
let aa = 1
console.log(aa)
}
aaa()
}
</script>

</body>
</html>

(完)

分享到

箭头函数中的this

上一次学习箭头函数, 只是懂它的写法, 但不是很清楚他的语法, 所以这次重新学习了箭头函数, 也算全部都搞懂了

  1. 箭头函数没有自己的this ,箭头函数的this不是调用的时候决定的, 而是取决于你定义的这个对象 。

  2. 扩展: 如果箭头函数外面还存在函数

    • 若是普通函数,则箭头函数中的this为普通函数的this
    • 若是箭头函数, 则箭头函数中的this为Windows
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button id="b1">1</button>
<button id="b2">2</button>
<button id="b3">3</button>
</body>
<script>
let aaa = document.getElementById('b1')
let bbb = document.getElementById('b2')
let ccc = document.getElementById('b3')
let obj = {
a: 123,
b: 321,
c: this.a,
o: () => {
aaa.onclick = () => {
console.log(this)
}
},
oo: oo = () => {
bbb.onclick = () => {
console.log(this)
}
ccc.onclick = () => {
console.log(this)
}
}
}
obj.o()
obj.oo()

//执行结果都为Window

</script>

</html>

(完)

分享到