一、 简介

1. websocket 推送技术在 2008 诞生,2011 成为标准,现在所有浏览器都支持了。

2. 最大特点 是真正的双向平等对话,属于服务器推送技术的一种

扩展:服务器推送技术 

1. webpush 

Internet工程任务组的Webpush提议是一种使用HTTP版本2的简单协议,用于提供可以及时传递(或“推送”)的实时事件,例如传入呼叫或消息。该协议将所有实时事件合并到一个会话中,以确保更有效地使用网络和无线电资源。单个服务整合所有事件,在应用程序到达时将这些事件分发给应用程序。这只需要一个会话,避免重复的开销成本。

2. HTTP 服务器推送

HTTP服务器推送(也称为HTTP流)是一种用于将未经请求的(异步)数据从Web服务器发送到Web浏览器的机制。HTTP服务器推送可以通过几种机制中的任何一种来实现。

作为HTML5的一部分,WebSocket API允许Web服务器和客户端通过全双工 TCP连接进行通信。

3. pushlet 

在这种技术中,服务器利用持久的HTTP连接,使响应永久“打开”(即,服务器永远不会终止响应),有效地欺骗浏览器在初始页面加载被考虑之后保持“加载”模式完成。然后,服务器定期发送JavaScript片段以更新页面内容,从而实现推送功能。通过使用这种技术,客户端不需要Java applet或其他插件来保持与服务器的开放连接; 客户端会自动通知服务器推送的新事件。[11] [12]然而,这种方法的一个严重缺点是服务器缺乏对浏览器超时的控制; 如果浏览器端发生超时,则始终需要页面刷新。

4. 长轮询

5. Flash XMLSockt 中继

Cbox和其他聊天应用程序使用的这种技术在单像素Adobe Flash电影中使用了XMLSocket对象。在控制的JavaScript,客户端建立一个TCP连接到一个单向的服务器上的继电器。中继服务器不从该套接字读取任何内容; 相反,它立即向客户端发送唯一标识符。接下来,客户端发出HTTP请求到web服务器,包括它的这个标识符。然后,Web应用程序可以将发往客户端的消息推送到中继服务器的本地接口,中继服务器通过Flash套接字对它们进行中继。这种方法的优点在于它欣赏许多Web应用程序(包括聊天)的典型自然读写不对称性,因此它提供了高效率。因为它不接受即将离任的插座数据,中继服务器不需要轮询传出TCP连接,在所有的,从而能够容纳的并发连接打开数万人。在此模型中,规模限制是底层服务器操作系统的TCP堆栈。

6. 可靠的集团数据交付(RGDD)

在云计算等服务中,为了提高数据的可靠性和可用性,通常会将其推送(复制)到多台计算机上。例如,Hadoop分布式文件系统(HDFS)为存储的任何对象生成2个额外副本。RGDD专注于有效地将对象从一个位置转换为多个位置,同时通过在网络上的任何链接上发送对象的最小数量的副本(在最佳情况下只有一个)来节省带宽。例如,Datacast [15]是一种向数据中心内的许多节点传递的方案,它依赖于常规和结构化拓扑,DCCast [16]是一种跨数据中心交付的类似方法。

7. 推送通知

推送通知是从后端服务器或应用程序“推送”到用户界面的消息,例如(但不限于)移动应用程序和桌面应用程序。Apple于2009年首次推出推送通知。[17] 2010年,Google发布了自己的服务,即Google Cloud to Device Messaging。(此后它已被Google云消息传递Firebase云消息传递所取代。)[18] 2015年11月,微软宣布推出Windows通知服务将扩展为使用通用Windows平台架构,允许使用通用API调用和POST请求将推送数据发送到Windows 10,Windows 10移动版,Xbox以及其他支持的平台。[19]

特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

ws://example.com:80/some/path


二、客户端示例

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};    


三、客户端 API

4.1 构造函数

var ws = new WebSocket('ws://localhost:8080');

4.2 webSocket.readyState

readyState 属性返回实例对象的当前状态,共有四种

CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

示例

switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

4.3 webSocket.onopen

实例对象的 onopen 属性,用于指定连接成功后的回调函数

ws.onopen = function () {
  ws.send('Hello Server!');
}

如果指定多个回调函数,可以使用 addEventListnenr 方法

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});

4.4 webSocket.onclose 

实例对象的 onclose 属性,用于指定连接关闭后的回调函数

ws.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
};

ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});

4.5 webSocket.onmessage

 实例对象的 onmessage 属性,用于指定收到服务器数据后的回调函数

ws.onmessage = function(event) {
  var data = event.data;
  // 处理数据
};

ws.addEventListener("message", function(event) {
  var data = event.data;
  // 处理数据
});

注意,服务器数据可能是文本,也可能是二进制数据(blob 对象或 Arraybuffer 对象)

ws.onmessage = function(event){
  if(typeof event.data === String) {
    console.log("Received data string");
  }

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

出来动态判断收到的数据类型,也可以使用 binaryType 属性,显式指定收到的二进制数据类

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

型。

// 收到的是 blob 数据
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

// 收到的是 ArrayBuffer 数据
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};

4.6 webSocket.sed()

实例对象的 send() 方法用于向服务器发送数据

发送文本的例子

ws.send('your message');

发送 Blob 对象的例子

var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);

发送 ArrayBuffer 对象的例子

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);

4.7 webSocket.bufferedAmount

实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

4.8 webSocket.onerror 

实例对象的onerror属性,用于指定报错时的回调函数。

socket.onerror = function(event) {
  // handle error event
};

socket.addEventListener("error", function(event) {
  // handle error event
});

1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键 

    < table border oncontextmenu=return(false)>< td>no< /table> 可用于 Table 

2. < body onselectstart="return false"> 取消选取、防止复制 
 
3. onpaste="return false" 不准粘贴 
 
4. oncopy="return false;" oncut="return false;" 防止复制 
 
5. < link rel="Shortcut Icon" href="favicon.ico"> IE 地址栏前换成自己的图标 
 
6. < link rel="Bookmark" href="favicon.ico"> 可以在收藏夹中显示出你的图标 
 
7. < input style="ime-mode:disabled"> 关闭输入法 
 
8. 永远都会带着框架 < script language="JavaScript">< !-- if (window == top)top.location.href = "frames.htm"; //frames.htm 为框架网页 // -->< /script> 
 
9. 防止被人 frame < SCRIPT LANGUAGE=JAVASCRIPT>< !--  if (top.location != self.location)top.location=self.location; // -->< /SCRIPT> 
 
10. 网页将不能被另存为 

<noscript>< iframe src=*.html>< /iframe></noscript>  

11.  查看网页源代码

<input type=button value=查看网页源代码 onclick="window.location='view-source:' + window.location.href">

12.删除时确认 

 <a href="javascript:if(confirm('确实要删除吗?'))location='boos.asp?&areyou=删除&page=1'">删除</a>

13. 取得控件的绝对位置  // Javascript

    function getIE(e) {
        var t = e.offsetTop; var l = e.offsetLeft; while (e = e.offsetParent) {
            t += e.offsetTop; l += e.offsetLeft;  } alert("top=" + t + "/nleft=" + l);
    } 

//VBScript < script language="VBScript">< !-- function getIE() dim t,l,a,b set a=document.all.img1 t=document.all.img1.offsetTop l=document.all.img1.offsetLeft while a.tagName< >"BODY" set a = a.offsetParent t=t+a.offsetTop l=l+a.offsetLeft wend msgbox "top="&t&chr(13)&"left="&l,64,"得到控件的位置" end function -->< /script> 
 

14. 光标是停在文本框文字的最后

    <input type=text name=text1 value=”123″ onfocus="cc()">
    <script language=”javascript”>
        function cc() {
            var e = event.srcElement;
            var r = e.createTextRange();
            r.moveStart("character", e.value.length);
            r.collapse(true);
            r.select();
        }
    </script>

15. 判断上一页的来源 

 document.referrer 

16. 最小化、最大化、关闭窗口 < object id=hh1 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">  < param name="Command" value="Minimize">< /object> < object id=hh2 classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">  < param name="Command" value="Maximize">< /object> < OBJECT id=hh3 classid="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11"> 
< PARAM NAME="Command" VALUE="Close">< /OBJECT> < input type=button value=最小化 onclick=hh1.Click()> < input type=button value=最大化 onclick=hh2.Click()> < input type=button value=关闭 onclick=hh3.Click()> 本例适用于 IE 
 

17.屏蔽功能键 

 function look() {
      if (event.shiftKey) alert("禁止按 Shift 键!"); //可以换成 ALT CTRL 
 }
document.onkeydown = look;

 

18. 网页不会被缓存 

<META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"> <META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT"> 或者<META HTTP-EQUIV="expires" CONTENT="0"> 

19.怎样让表单没有凹凸感? 

<input type=text style="border:1 solid #000000">  
或 
<input type=text style="border-left:none; border-right:none; border-top:none; border-bottom: 1px solid #000000">< /textarea> 

 
20.< div>< span>&< layer>的区别?  < div>(division)用来定义大段的页面元素,会产生转行  < span>用来定义同一行内的元素,跟< div>的唯一区别是不产生转行  < layer>是 ns 的标记,ie 不支持,相当于< div> 
 

21.让弹出窗口总是在最上面: 

<body onblur="this.focus();"> 

22.不要滚动条?

//  让竖条没有:  
<body style="overflow:scroll;overflow-y:hidden">  </body> 
// 让横条没有: 
 <body style="overflow:scroll;overflow-x:hidden">  </body>  
// 两个都去掉?更简单了  
<body scroll="no">  </body>  

23.怎样去掉图片链接点击后,图片周围的虚线?

 <a href="#" onFocus="this.blur()"><img src="logo.jpg" border=0></a> 

24.电子邮件处理提交表单 

<form name="form1" method="post" action="mailto:****@***.com" enctype="text/plain">  
   <input type=submit> 
</form> 

 
25.在打开的子窗口刷新父窗口的代码里如何写? window.opener.location.reload()  
 
26.如何设定打开页面的大小 < body onload="top.resizeTo(300,200);"> 打开页面的位置< body onload="top.moveBy(300,200);"> 
 
27.在页面中如何加入不是满铺的背景图片,拉动页面时背景图不动  < STYLE>  body  {background-image:url(logo.gif); background-repeat:no-repeat;  background-position:center;background-attachment: fixed}  < /STYLE>  
 
28. 检查一段字符串是否全由数字组成 < script language="Javascript">< !-- function checkNum(str){return str.match(//D/)==null} alert(checkNum("1232142141")) alert(checkNum("123214214a1")) // -->< /script> 
 
29. 获得一个窗口的大小 document.body.clientWidth; document.body.clientHeight 
 
30. 怎么判断是否是字符 if (/[^/x00-/xff]/g.test(s)) alert("含有汉字"); else alert("全是字符"); 
 
31.TEXTAREA 自适应文字行数的多少 < textarea rows=1 name=s1 cols=27 onpropertychange="this.style.posHeight=this.scrollHeight"> < /textarea> 
 
32. 日期减去天数等于第二个日期 
< script language=Javascript> function cc(dd,dadd) { //可以加上错误处理 var a = new Date(dd) a = a.valueOf() a = a - dadd * 24 * 60 * 60 * 1000 a = new Date(a) alert(a.getFullYear() + "年" + (a.getMonth() + 1) + "月" + a.getDate() + "日") } cc("12/23/2002",2) < /script> 
 
33. 选择了哪一个 Radio < HTML>< script language="vbscript"> function checkme() for each ob in radio1 if ob.checked then window.alert ob.value next end function < /script>< BODY> < INPUT name="radio1" type="radio" value="style" checked>Style < INPUT name="radio1" type="radio" value="barcode">Barcode < INPUT type="button" value="check" onclick="checkme()"> < /BODY>< /HTML> 
 
34.脚本永不出错 < SCRIPT LANGUAGE="JavaScript">  < !-- Hide  function killErrors() {  return true;  }  window.onerror = killErrors;  // -->  < /SCRIPT> 
 
35.ENTER 键可以让光标移到下一个输入框 <input onkeydown="if(event.keyCode==13)event.keyCode=9"> 
 
36. 检测某个网站的链接速度: 把如下代码加入< body>区域中: < script language=Javascript> tim=1 setInterval("tim++",100) 
b=1 var autourl=new Array() autourl[1]="www.njcatv.net" autourl[2]="javacool.3322.net" autourl[3]="www.sina.com.cn" autourl[4]="www.nuaa.edu.cn" autourl[5]="www.cctv.com" function butt(){ document.write("< form name=autof>") for(var i=1;i< autourl.length;i++) document.write("< input type=text name=txt"+i+" size=10 value=测试中……> =》< input type=text  name=url"+i+" size=40> =》< input type=button value=GO  
onclick=window.open(this.form.url"+i+".value)> ") document.write("< input type=submit value=刷新>< /form>") } butt() function auto(url){ document.forms[0]["url"+b].value=url if(tim>200) {document.forms[0]["txt"+b].value="链接超时"} else {document.forms[0]["txt"+b].value="时间"+tim/10+"秒"} b++ } function run(){for(var i=1;i< autourl.length;i++)document.write("< img src=http://"+autourl+"/"+Math.random()+"  
 
width=1 height=1  
 
onerror=auto("http://"+autourl+"")>")} run()< /script> 
 
37. 各种样式的光标 auto :标准光标 default :标准箭头 hand :手形光标 wait :等待光标 text :I 形光标 vertical-text :水平 I 形光标 no-drop :不可拖动光标 not-allowed :无效光标 
help :?帮助光标 all-scroll :三角方向标 move :移动标 crosshair :十字标 e-resize n-resize nw-resize w-resize s-resize se-resize sw-resize 
 
38.页面进入和退出的特效 进入页面< meta http-equiv="Page-Enter" content="revealTrans(duration=x, transition=y)"> 推出页面< meta http-equiv="Page-Exit" content="revealTrans(duration=x, transition=y)">   这个是页面被载入和调出时的一些特效。duration 表示特效的持续时间,以秒为单位。 transition 表示使用哪种特效,取值为 
 
1-23:   0 矩形缩小    1 矩形扩大    2 圆形缩小   3 圆形扩大    4 下到上刷新    5 上到下刷新   6 左到右刷新    7 右到左刷新    8 竖百叶窗   9 横百叶窗    10 错位横百叶窗    11 错位竖百叶窗   12 点扩散    13 左右到中间刷新    14 中间到左右刷新   15 中间到上下   16 上下到中间    17 右下到左上   18 右上到左下    19 左上到右下    20 左下到右上   21 横条    22 竖条    23 以上22 种随机选择一种 
 
39.在规定时间内跳转 < META http-equiv=V="REFRESH" content="5;URL=http://www.51js.com">  
 
40.网页是否被检索 < meta name="ROBOTS" content="属性值">   其中属性值有以下一些:   属性值为"all": 文件将被检索,且页上链接可被查询;   属性值为"none": 文件不被检索,而且不查询页上的链接;   属性值为"index": 文件将被检索;   属性值为"follow": 查询页上的链接;   属性值为"noindex": 文件不检索,但可被查询链接;   属性值为"nofollow": 文件不被检索,但可查询页上的链接。 
 
41.回车 用客户端脚本在页面添加document的onkeydown事件,让页面在接受到回车事件后,进行Tab 键的功能,即只要把 event 的 keyCode 由 13 变为 9 Javascript 代码如下: 
 
<script language="javascript" for="document" event="onkeydown"> <!--   if(event.keyCode==13)      event.keyCode=9; --> </script> 这样的处理方式,可以实现焦点往下移动,但对于按钮也起同样的作用,一般的客户在输入完 资料以后,跳到按钮后,最好能直接按"回车"进行数据的提交.因此,对上面的方法要进行一下 修改,应该对于"提交"按钮不进行焦点转移.而直接激活提交. 因此我对上面的代码进行了一个修改,即判断事件的"源",是否为提交按钮,代码如下: <script language="javascript" for="document" event="onkeydown"> <!--   if(event.keyCode==13 && event.srcElement.type!='button' && event.srcElement.type!='submit' && event.srcElement.type!='reset' && event.srcElement.type!='textarea' && event.srcElement.type!='')      event.keyCode=9; --> </script> 判断是否为 button, 是因为在 HTML 上会有 type="button" 判断是否为 submit,是因为 HTML 上会有 type="submit" 判断是否为reset,是因为 HTML 上的"重置"应该要被执行 判断是否为空,是因为对于 HTML 上的"<a>链接"也应该被执行,这种情况发生的情况不多,可 以使用"tabindex=-1"的方式来取消链接获得焦点. 

在 css 样式上 -webkit-box-orient:vertical;  加上注释, 

如下:

/* autoprefixer: off */
 -webkit-box-orient: vertical; 
 /* autoprefixer: on */

打包时必须使用这种方法打包,否则打包后  -webkit-box-orient: vertical 便会消失

网上解决方案是这样的,但是我在我的项目中发现不起作用,

解决方案

optimize-css-assets-webpack-plugin这个插件的问题
注释掉webpack.prod.conf.js中下面的代码

new OptimizeCSSPlugin({
  cssProcessorOptions: config.build.productionSourceMap
    ? { safe: true, map: { inline: false } }
    : { safe: true }
}),

组件传值

1. 父向子组件传值,和传递方法

父组件 app-home 

子组件 app-header

// --- app-home.html 页面

<app-header [msg]="msg" [run]='run' [getDataFromChild]="getDataFromChild"></app-header>


// --- app-home.ts 

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: './app-home.html',
  styleUrls: ['./app-home.css']
})
export class HomeComponent implements OnInit {

  public msg="我是home组件的msg";

  constructor() { }

  ngOnInit() {
  }

  run(){

    alert('这是home组件的run方法');
  }

  getDataFromChild(childData){  /*父组件*/
    alert(childData+"1111");
  }

}


// --- app.header.ts 页面

import { Component, OnInit,Input} from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './app-header.html',
  styleUrls: ['./app-header.css']
})
export class FooterComponent implements OnInit {

  @Input()  msg;


  @Input()  run;  /*接收父组件传过来的run方法*/

  @Input()  getDataFromChild; 


  public msginfo='这是子组件的数据';

  constructor() { }

  ngOnInit() {
  }

  sendParent(){  /*子组件自己的方法*/
      this.getDataFromChild(this.msginfo);  /*子组件调用父组件的方法*/

  }

}

1.2 子组件用广播方式调用父组件方法

// --- app-home.html

<app-footer [msg]="msg" [run]='run' (toparent)="getDataFromChild"></app-footer>

// --- app-home.ts 

import { Component, OnInit } from '@angular/core';
import {Http,Jsonp} from "@angular/http";
@Component({
  selector: 'app-news',
  templateUrl: './app-home.html',
  styleUrls: ['./app-home.css']
})
export class NewsComponent implements OnInit {
   public msg="msg";
   public name="张三";

   public list=[];
  constructor(private http:Http,private jsonp:Jsonp) { }

  ngOnInit() {
  }

  getDataFromChild(childData){  /*父组件*/
    alert(childData+"1111");
  }
}

//--- app-header.ts

import { Component, OnInit,Input,Output,EventEmitter } from '@angular/core';

@Component({
  selector: 'app-header',
  templateUrl: './app-header.html',
  styleUrls: ['./app-header.css']
})
export class HeaderComponent implements OnInit {

   @Input()  msg:string;  /*通过 Input  接收父组件传过来的msg*/

   @Input()  name:string; 


   //EventEmitter实现子组件给父组件传值

   @Output() toparent=new EventEmitter();

  constructor() { }
  ngOnInit() {}

  //requestData

  requestData(){

    //调用父组件的方法请求数据

    this.toparent.emit('这是子组件的值');
  }

}

2. 父组件获取子组件值

// app-home.html

<app-header #header></app-header>

// app-home.ts

//ViewChild
import { Component, OnInit, ViewChild } from "@angular/core";

@Component({
  selector: "app-home",
  templateUrl: "./app-home.html",
  styleUrls: ["./app-home.css"]
})
export class ProductComponent implements OnInit {
  @ViewChild("header") header; /*定义子组件 注意括号里面的东西和 #header 对应起来  */

  constructor() {}

  ngOnInit() {}

  getChildData() {
    // this.header.run();   /*执行子组件的方法*/

    alert(this.header.msg); /*获取子组件的数据*/

    //  alert(this.header.name);

    this.header.name = "我是父组件改变后的cart name";
  }
}

// app-header.ts

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-header",
  templateUrl: "./app-header.html",
  styleUrls: ["./app-header.css"]
})
export class CartComponent implements OnInit {
  constructor() {}

  public msg = "我是子组件的数据";

  public name = "子组件";

  ngOnInit() {}

  run() {
    alert("这是子组件的方法11");
  }
}

路由之间传参

在网上找到一个不错的写的,分享给大家:

一般我们页面跳转传递参数都是这样的格式:

http://angular.io/api?uid=1&username=moon

但是在SPA单页应用中却是下面的结果居多【初级视频都是这样敷衍的】

http://angular.io/api/1/moon

一、 地址栏传参

实现从product页面跳转到product-detail页面

1:在app-routing.module.ts中配置路由。

const routes: Routes = [
{
    path: 'product',
    component: ProductComponent,
 },
 {
    path: 'product-detail',
    component: ProductDetailComponent,
  }
];

2:在app-routing.module.ts中配置路由。

constructor(
    private router: Router,   //这里需要注入Router模块
){}
 
jumpHandle(){
    //这是在html中绑定的click跳转事件
    this.router.navigate(['product-detail'], {
        queryParams: {
            productId: '1',
            title: 'moon'
        }
    });
}


3:在product-detail.ts中获取传递过来的参数productId、title

constructor(
    private activatedRoute: ActivatedRoute,   //这里需要注入ActivatedRoute模块
) {
    activatedRoute.queryParams.subscribe(queryParams => {
        let productId = queryParams.productId;
        let title = queryParams.title;
    });
}


二、 在查询中传递参数

 



//传递数据
<a [routerLink]="['/stock']" [queryParams]="{id: 1}">股票详情</a>
// http://localhost:4200/stock?id=1



// 接受参数
import { ActivatedRoute } from '@amgular/router';

export class StockComponent implements OnInit {

    private stockId: number;    
    
    constructor(private routeInfo: ActivatedRoute)
    
    ngOnInit() {
        this.stockId = this.routeInfo.snapshot.queryParams['id'];
    }
    
}

三、 在路径中传递参数

//修改配置
const routes: Routes = [
  {path: '', redirectTo: '/index', pathMatch: 'full'},
  {path: 'index', component: IndexComponent},
  {path: 'stock/:id', component: StocksComponent },
  {path: '**', component: ErrorPageComponent }
];


//传递数据
...
<a [routerLink]="['/stock', 1]">股票详情</a>
// http://localhost:4200/stock/1



//接受参数
...
import { ActivatedRoute } from '@amgular/router';

export class StockComponent implements OnInit {

    private stockId: number;    
    
    constructor(private activedRoute: ActivatedRoute)
    
    ngOnInit() {
        this.stockId = this.activedRoute.snapshot.params['id'];
    }
    
}

使用snapshot快照的方式传递数据,因为初始化一次,路由到自身不能传递参数,需要使用订阅模式。

this.activedRoute.params.subscribe((params: Params) => this.stockId = params['id']);

 

四、在路由中配置

//路由配置配置
const routes: Routes = [
  {path: '', redirectTo: '/index', pathMatch: 'full'},
  {path: 'index', component: IndexComponent, data: {title: 'Index Page'}},
  {path: 'stock/:id', component: StocksComponent, data: {title: 'Stock Page'}},
  {path: '**', component: ErrorPageComponent, data: {title: 'Stock Page'}}
];

//接受参数
this.title = this.routeInfo.snapshot.date[0]['title'];

最近学习了 angular 表单。

1. 表单绑定数据,目前有两种方法:

    1. [(ngModel)]="name";  // 双向绑定属性值

    2. formControlNam="name";  // 按名称FormControl将现有FormGroup的a 同步到表单控件元素。

例如:

import {Component} from '@angular/core';
import {FormControl, FormGroup, Validators} from '@angular/forms';
 
@Component({
  selector: 'example-app',
  template: `
    <form [formGroup]="form" (ngSubmit)="onSubmit()"> <!-- 这里必须写 [formGroup]="form" 绑定下方 new FormGroup()  -->
      <div *ngIf="first.invalid"> Name is too short. </div>
 
      <input formControlName="first" placeholder="First name"> <!-- formControlName 绑定的是下方 new FormGroup() 中 first -->
      <input formControlName="last" placeholder="Last name">
 
      <button type="submit">Submit</button>
   </form>
   <button (click)="setValue()">Set preset value</button>
  `,
})
export class SimpleFormGroup {
  form = new FormGroup({
    first: new FormControl('Nancy', Validators.minLength(2)),  last: new FormControl('Drew'), // 'Nancy' 是 值, validators.minLength(2) 是angular默认表单验证,也可以自定义验证
  });
 
  get first(): any { return this.form.get('first'); } // 必须写 这个方法,之后才可以获取到表单的值
 
  onSubmit(): void {
    console.log(this.form.value);  // {first: 'Nancy', last: 'Drew'} // 获取 值
  }
 
  setValue() { this.form.setValue({first: 'Carson', last: 'Drew'}); } // 改变 first 与 last 表单的值
}

app.module.ts 引入必须 

import { FormsModule, ReactiveFormsModule } from "@angular/forms";

否者 不可以用 formControlName

注:formControlName 与 ngModel 使用

在Angular v6中不支持将ngModel输入属性和ngModelChange事件与反应式表单指令一起使用,并且将在Angular v7中删除。

现已弃用:

content_copy<form [formGroup]="form"> <input formControlName="first" [(ngModel)]="value"> </form> <!-- 不要一起用了 -->
content_copythis.value = 'some value';

由于一些原因,这已被弃用。首先,开发人员发现这种模式令人困惑。看起来似乎ngModel正在使用实际的指令,但实际上它是ngModel在反应式表单指令上命名的输入/输出属性,它简单地近似(某些)其行为。具体来说,它允许获取/设置值和拦截值事件。然而,ngModel其他一些功能 - 比如延迟更新ngModelOptions或导出指令 - 根本不起作用,这可以理解地引起一些混乱。

此外,这种模式混合了模板驱动和反应形式策略,我们通常不建议这样做,因为它没有利用这两种策略的全部好处。在模板中设置值违反了反应形式背后的模板无关原则,而在类中添加FormControlFormGroup层则消除了在模板中定义表单的便利性。

类型

var a = 1; 隐式; var a:number = 1; 显示声明

var arr = []

类型:

基本类型:

联合类型:----- 选择

函数签名:

success:(a, b)=>void;

复合类型(Object Type):

var a:{x:xx, y:xx};

number, string, boolean, null, undefined, enum, any;

函数类型:

  1. 参数

    • 函数签名
  2. 返回值

function show(a:number, b:number)


外部变量

declare var $;


接口-interface

  • 和 java 不同
  • 接口:约定、限制;

真面向对象

class 写法, extend, 多继承

访问修饰符:

  1. public 共有 任何人可以访问
  2. private 私有 只有类内部可以访问
  3. protected 受保护-友元 只有子类可以访问

泛型-- 别跟 “any” 搞混了 泛-- 宽泛

class Calc<T> {
    a:T;
    b:T;
    show (c  :T) {
        alert(a + b + c)
    }
} var obj = new Calc<number>();

obj.a = 12;
obj.b = 'abc'; // 报错 }

Array --- 泛型

interface Array<T> { 

    reverse() : T[]; 

    sort(compare?: a(a:T, b:T) =>number):T[];

    ...

}

// 泛型:类型当参数传入

数组完成写法:var arr:Array<number> = new Array<number>();

layout 响应式布局工具

  • Breakpoints 屏幕尺寸
  • BreakpointObserver 观察器
  • MediaMatcher 媒体查询匹配

import { Component, OnInit } from "@angular/core";
import {
  BreakpointObserver,
  BreakpointState,
  MediaMatcher,
  Breakpoints
} from "@angular/cdk/layout";
import { Observable } from "rxjs/Observable";
@Component({
  selector: "layout",
  templateUrl: "./layout.component.html",
  styleUrls: ["./layout.component.scss"]
})
export class LayoutComponent implements OnInit {
  isHandset: Observable<BreakpointState>;
  constructor(public breakpointObserver: BreakpointObserver, public media: MediaMatcher) {}

  ngOnInit() {
    // 手持设备
    let Handset = Breakpoints.Handset;
    // 手持landscape屏
    let HandsetLandscape = Breakpoints.HandsetLandscape;
    //手持portrait屏
    let HandsetPortrait = Breakpoints.HandsetPortrait;
    // 多媒体
    let Medium = Breakpoints.Medium;
    // 平板电脑
    let Tablet = Breakpoints.Tablet;
    // 平板电脑 Landscape
    let TabletLandscape = Breakpoints.TabletLandscape;
    // 平板电脑 Portrait
    let TabletPortrait = Breakpoints.TabletPortrait;
    // web
    let Web = Breakpoints.Web;
    // web landscape
    let WebLandscape = Breakpoints.WebLandscape;
    // web portrait
    let WebPortrait = Breakpoints.WebPortrait;
    // 大屏幕
    let Large = Breakpoints.Large;
    // 更大屏幕
    let XLarge = Breakpoints.XLarge;
    // 更小屏幕
    let XSmall = Breakpoints.XSmall;
    // 小屏幕
    let Small = Breakpoints.Small;

    // 是否满足多个条件
    this.breakpointObserver
      .observe([
        Handset,
        HandsetLandscape,
        Handset,
        Medium,
        Tablet,
        TabletLandscape,
        TabletPortrait,
        Web,
        WebLandscape,
        WebPortrait,
        Large,
        XLarge,
        Small,
        XSmall
      ])
      .subscribe(res => {
        console.log(res);
      });
      this.breakpointObserver
      .observe(Breakpoints.Handset)
        .subscribe((state: BreakpointState) => {
        if (state.matches) {
          console.log('手机设备尺寸内');
        } else {
          console.log('非手机设备尺寸内');
        }
      });

  }
}

angular6

1. 简单介绍下项目开始,官方文档上都有详细介绍

1. 然后全局安装 angularCLI

npm install -g @angular/cli

2. 用脚手架创建项目

ng new my-app 

这样就生成一个项目

3. 进入项目目录运行服务并打开

cd my-app
ng serve --open

注:项目目录官方文档很详细我就不介绍了

2. 创建组件

ng generate component heroes

会在 app 文件下创建 一个 hreoes 组件并将其引入到 app.moduls.ts 根文件

hreoes 组件文件夹:文件夹目录下会有四个文件,css 样式文件,html 文件,ts 文件,剩下一个文件没有用.

// 用 ng generate component 创建组件过多时,app.mosuls.ts 会出现乱码或引入错误代码跑偏,需要自己整理!!!

@Component 是个装饰器函数,用于为该组件指定 Angular 所需的元数据。

CLI 自动生成了三个元数据属性:

  1. selector— 组件的选择器(CSS 元素选择器)

  2. templateUrl— 组件模板文件的位置。

  3. styleUrls— 组件私有 CSS 样式表文件的位置。

还有很多扩展属性:
    1. encapsulation: 

      2. changeDetection: 实例化组件时,Angular会创建一个更改检测器

      3.  viewProviders定义其视图DOM子项可见的可注入对象集。

      4. moduleId ,

      5. templateUrl 与 template :不用多说指向 html ,templateUrl 指向 html 路径, template 直接来写 html

      6. styleUrls 与 styles :同上

      7. animations :动画,很厉害的东西,官方介绍通过类似动画的DSL在组件上定义动画。这种描述动画的DSL方法允许灵活性,既有利于开发人员,也有利于框架。动画通过侦听模板中元素上发生的状态更改来工作。当发生状态变化时,Angular可以利用并在其间设置动画。这与CSS转换的工作方式类似,但是,通过编程DSL,动画不仅限于特定于DOM的环境。(Angular也可以在幕后执行优化,使动画效果更高。)要使动画可供使用,动画状态更改将放置在动画触发器中,动画触发器位于动画注释元数据内。在触发器内,可以放置状态和转换条目。

      8. interpolation:覆盖默认的封装开始和结束分隔符 (respectively {{ and }})

      9. entryComponents:定义在定义此组件时应编译的组件。对于此处列出的每个组件,Angular将创建一个ComponentFactory并将其存储在ComponentFactoryResolver中。

       10. preserveWhitespaces: boolean 

CSS 元素选择器 app-heroes 用来在父组件的模板中匹配 HTML 元素的名称,以识别出该组件。

ngOnInit 是一个生命周期钩子,Angular 在创建完组件后很快就会调用 ngOnInit。这里是放置初始化逻辑的好地方。

始终要 export 这个组件类,以便在其它地方(比如 AppModule)导入它。

3. 创建服务

ng generate service hero

使用 Angular CLI 创建一个名叫 hero 的服务。

@Injectable() 服务


注意,这个新的服务导入了 Angular 的 Injectable 符号,并且给这个服务类添加了 @Injectable() 装饰器。 它把这个类标记为依赖注入系统的参与者之一。HeroService 类将会提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖。 目前它还没有依赖,但是很快就会有了

@Injectable() 装饰器会接受该服务的元数据对象,就像 @Component() 对组件类的作用一样。

mysql 常用语法

1. SQL 语句分类

  • DDL (Data DefinitonLanguage) 数据定义语言,用来数据库对象:库、表、列等。功能:创建、删除、修改库和表结构。
  • DML (Data Manipulation Language) 数据操作语言,用来蒂尼数据库记录,增、删、改表记录。
  • DCL  (Data Control Language) 数据控制语言,用来定义访问权限和安全级别。
  • DQL (Data Query Language) 数据查询语言,用来查询记录。

2. SQL 数据中的属性类型

  • tintiny:1 字节,小整数值。
  • smallint:2 字节,大整数值。
  • mediumint:3 字节,大整数值。
  • int或 integer:4 字节,整型,大整数值。
  • float单精度浮点数值。
  • double(5.2) 双精度浮点型数值,参数表示该浮点型数值最多有5位,其中必须有2位小数。
  • decimal(M,D) 小数值,参数表示该数值最多有M位,其中必须有D位小数。
  • char: 字符型,固定长度字符串类型: char(255)。 你存入一个 a 字符, 虽然 a 只占一个字符, 但是它会自动给你加 254 个空格凑成 255 个长度。即数据的长度不足指定长度,它会补足到指定长度。
  • varchar 可变长度字符串类型:-varchar(65535), 你存入的数据多长它就是多长。它会抽出几个字节来记录数据的长度。
  • text (CLOB):mysql 独有的数据类型,字符串类型。
  • blob:字节类型
  • year: 年份值,格式为:YYYY
  • date: 日期类型,格式为 yyy-mm-dd.
  • time 时间类型,格式为: hh:mm:ss。
  • timestamp: 时间戳类型,格式为上面二者的综合。
  • datetime: 混合日期和时间值,格式为:YYYYMMDD HHMMSS

3. SQL 语句详解

首先需要在命令行中输入 mysql -u root -p 来进入 mysql 。注意:1、mysql 语法不区分大小写,但是建议在写关键字时用大写。2. 每一条语句后面以分号结尾。

3.1 DDL (数据定义语言)语法

该语言用来对数据库和表结构进行操作。

对数据的操作:

查看所有的数据库 show databases:

使用书库 use 数据名,

创建数据库并制定编码: create database [if not exists] 数据库名 [defaultcharacter set utf8];

删除数据库:drop database 数据库名;

修改数据库的编码:alter database 数据库名 character set utf-8

对表结构的操作

创建表:

create table (if not exists) 表名 (
列名,列类型, 
...,
列名,列类型,
)
  • 查看当前数据库中所有表:show tables;
  • 查看表结构:desc 表名;
  • 删除表:drop 表名;
  • 修改表:修改表有5个操作,但前缀都是一样的: alter table 表名...
  • 修改表之添加列:alter table 表名 add (列名 列类型, ..., 列名 列类型);
  • 修改表之列名称:alter table 表名 modify 列名 列的新类型;
  • 修改表之列名称列类型一起修改:alter table 表名 change 原列名 新列名 列名类型;
  • 修改表之删除列:alter table 表名 drop 列名;
  • 修改表之修改表名:alter table 表名 rename to 新表名

3.2 DML (数据操作语法)语法

该语言用来记录操作(增,删,改)。

3.2.1 插入数据(一次插入就是一行)

insert into 表名 (列名1,列名2,列名3) value (列值1,列值2,列值3)

说明:

1. 在数据库中所有的字符串类型,必须使用单引号。

2. (列名1,列名2,列名3)可省略,表示按照表中的顺序插入。但不建议采取这种写法,因为降低了程序的可读性。

3. 在命令行插入记录不要写中文,否则会出现乱码(解决控制台的乱码问题后便可插入中文)

3.2.2 修改记录(不会修改一行)

修改某列的全部值: update 表名 set 列名1 = 列值1

修改(某行或者多行的记录的)列的指定值:update 表名 set 列名1=列值1 where 列名2=列值2 or 列名3=列值3;

运算符:=,!=,<>,<,>,>=, <=, between...and, in(...), is null, not, or, and,

 其中 in(...)的用法表示集合。 例如: update 表名 set 列名1=列值1 where 列名2=列值2 or 列名2=列值2 用 in(...) 写成 update 表名 set 列名1=列值1 where 列名2 in (列值2,列值3)

3.2.3 删除数据(删除整行)

delete from 表名 (where 条件);不加 where 条件时会删除表中所有的记录,所以为了防止这种失误操作,很多数据库往往都会有备份。

3.3 DCL(数据控制语言)语法

该语言用来定义访问权限,理解即可,以后不会多用。需要记住的是,一个项目创建一个用户,一个项目对应的数据库只有一个。这个用户只能对这个数据库有权限,其它数据库该用户就操作不了。

3.3.1创建用户

用户只能在指定ip地址上登录mysql:create user 用户名@IP地址 identified by ‘密码’;

用户可以在任意ip地址上登录:create user 用户名@‘%’ identified by ‘密码’;

3.3.2给用户授权

语法:grant 权限1,…,权限n on 数据库.* to 用户名@IP地址;其中权限1、2、n可以直接用all关键字代替。权限例如:create,alter,drop,insert,update,delete,select。

3.3.3撤销授权

语法:revoke 权限1,…,权限n on 数据库.* from 用户名@ ip地址;撤销指定用户在指定数据库上的指定权限。撤销例如:revoke create,delete on mydb1.* form user@localhost;表示的意思是撤消user用户在数据库mydb1伤的create、alter权限。

3.3.4查看权限

查看指定用户的权限:show grants for 用户名@ip地址;

3.4DQL(数据查询语言)语法

重点,该语言用来查询记录,不会修改数据库和表结构。

3.4.1基本查询(后缀都是统一为from 表名)
  • 1.字段(列)控制1.查询所有列:select * from 表名;
    其中*表示查询所有列,而不是所有行的意思。
  • 2.查询指定列:select 列1,列2,列n from 表名;
  • 3.完全重复的记录只显示一次:在查询的列之前添加distinct:select distinct $ from 表名;
    缺省值为all。
  • 4.列运算a.数量类型的列可以做加、减、乘、除:SELECT sal*5 from 表名;
说明:

1.遇到null加任何值都等于null的情况,需要用到ifnull()函数。
2.将字符串做加减乘除运算,会把字符串当作0。b.字符串累类型可以做连续运算(需要用到concat()函数):select concat(列名1,列名2) from 表名;其中列名的类型要为字符串。c. 给列名起别名:select 列名1 (as) 别名1,列名2 (as) 别名2 from 表名;

2.条件控制
1.条件查询。在后面添加where指定条件:select * from 表名 where 列名=指定值;
2.模糊查询:当你想查询所有姓张的记录。用到关键字like。eg:select * from 表名 where 列名 like ‘张_’;
(代表匹配任意一个字符,%代表匹配0~n个任意字符)。

3.4.2排序(所谓升序和降序都是从上往下排列)

1.升序:select * form 表名 order by 列名 (ASC );
()里面的内容为缺省值;
2.降序:select * from 表名 order by 列名 DESC;

3.使用多列作为排序条件: 当第一列排序条件相同时,根据第二列排序条件排序(当第二列依旧相同时可视情况根据第三例条件排序)。eg:select * from 表名 order by 列名1 ASC, 列名2 DESC;
意思是当列名1的值相同时按照列名2的值降序排。

3.4.3聚合函数

1.count:select count(列名) from 表名;
,纪录有效行数。
2.max:select count(列名) from 表名;
,列中最大值。
3.min:select sum(列名) from 表名;
,列中最小值。
4.sum:select sum(列名) from 表名;
,求列的总值,null 和字符串默认为0。
5.avg:select avg(列名) from 表名;
,一列的平均值。

3.4.4分组查询

分组查询的信息都是组的信息,不能查到个人的信息,其中查询组的信息是通过聚合函数得到的。
语法:select 分组列名,聚合函数1,聚合函数2 from 表名 group by 该分组列名;
其中分组列名需要的条件是该列名中有重复的信息。
查询的结果只能为:作为分组条件的列和聚合函数;查处的信息都是组的信息。
分组查询前,还可以通过关键字where先把满足条件的人分出来,再分组。语法为:select 分组列,聚合函数 from 表名 where 条件 group by 分组列;

分组查询后,也可以通过关键字having把组信息中满足条件的组再细分出来。语法为:select 分组列,聚合函数 from 表名 where 条件 group by 分组列 having 聚合函数或列名(条件);

3.4.5LIMIT子句(mysql中独有的语法)

LIMIT用来限定查询结果的起始行,以及总行数。
例如:select * from 表名 limit 4,3;
表示起始行为第5行,一共查询3行记录。

4.总结

学过的关键字:select,from,where,group by,having ,order by。
当一条查询语句中都包含所有这些关键字时它们的优先级是select>from>where>group by>having>order by

今天做锚点连接的时候,发现偶尔超链接出现偏移,在文档中找打了一个 :target  CSS选择器;

定义和用法

URL 带有后面跟有锚名称 #,指向文档内某个具体的元素。这个被链接的元素就是目标元素(target element)。

:target 选择器可用于选取当前活动的目标元素。

<!DOCTYPE html>
<html>
<head>
<style>
#news1:target
{
border: 2px solid #D4D4D4;
background-color: #e5eecc;
}
</style>
</head>
<body>

<h1>这是标题</h1>

<p><a href="#news1">跳转至内容 1</a></p>
<p><a href="#news2">跳转至内容 2</a></p>

<p>请点击上面的链接,:target 选择器会突出显示当前活动的 HTML 锚。</p>

<p id="news1"><b>内容 1...</b></p>
<p id="news2"><b>内容 2...</b></p>

<p><b>注释:</b> Internet Explorer 8 以及更早的版本不支持 :target 选择器。</p>

</body>
</html>

今天看到 一个 PHP 疯狂的吐槽,JavaScript 面试人员 JS 写的不如 PHP 连 AMD 与 CMD 都不会,我心里默默的想了一下,自己脑海里只是有个浅薄的概念,吓的我赶紧查查资料。

参考资料

     AMD 规范

     CMD 规范

    SeaJS 与 RequestJS 差异可以参考 https://github.com/seajs/seajs/issues/277

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
类似的还有 CommonJS Modules/2.0 规范,是 BravoJS 在推广过程中对模块定义的规范化产出。

这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。
目前这些规范的实现都能达成浏览器端模块化开发的目的

区别:

1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.

2. CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:

// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
// 此处略去 100 行
var b = require('./b') // 依赖可以就近书写
b.doSomething()
// ... 
})

// AMD 默认推荐的是
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
})

虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。

3. AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹

最近发现一个 好用的 node 调试方法

$ node - -inspect app.js  在平常启动项目的时候加上 --inspect 

 - -inspect参数是启动调试模式必需的。这时,打开浏览器访问

接下来,就要开始调试了。一共有两种打开调试工具的方法,第一种是在 Chrome 浏览器的地址栏,键入 chrome://inspect或者about:inspect,回车后就可以看到下面的界面。

在 Target 部分,点击 inspect 链接,就能进入调试工具了。

第二种进入调试工具的方法,是在 http://127.0.0.1:3000 的窗口打开”开发者工具”,顶部左上角有一个 Node 的绿色标志,点击就可以进入。

三、调试工具窗口
调试工具其实就是”开发者工具”的定制版,省去了那些对服务器脚本没用的部分。
它主要有四个面板。
Console:控制台
Memory:内存
Profiler:性能
Sources:源码
这些面板的用法,基本上跟浏览器环境差不多,这里只介绍 Sources (源码)面板。


四、设置断点
进入 Sources 面板,找到正在运行的脚本app.js。
在第11行(也就是下面这一行)的行号上点一下,就设置了一个断点。
这时,浏览器访问 http://127.0.0.1:3000/alice ,页面会显示正在等待服务器返回。切换到调试工具,可以看到 Node 主线程处于暂停(paused)阶段。
进入 Console 面板,输入 name,会返回 alice。这表明我们正处在断点处的上下文(context)。
再切回 Sources 面板,右侧可以看到 Watch、Call Stack、Scope、Breakpoints 等折叠项。打开 Scope 折叠项,可以看到 Local 作用域和 Global 作用域里面的所有变量。
Local 作用域里面,变量name的值是alice,双击进入编辑状态,把它改成bob。
然后,点击顶部工具栏的继续运行按钮。
页面上就可以看到 Hello bob 了。
命令行下,按下 ctrl + c,终止运行app.js。


五、调试非服务脚本
Web 服务脚本会一直在后台运行,但是大部分脚本只是处理某个任务,运行完就会终止。这时,你可能根本没有时间打开调试工具。等你打开了,脚本早就结束运行了。这时怎么调试呢?
$ node --inspect=9229 -e "setTimeout(function() { console.log('yes'); }, 30000)"
上面代码中,--inspect=9229指定调试端口为 9229,这是调试工具默认的通信端口。-e参数指定一个字符串,作为代码运行。
访问chrome://inspect,就可以进入调试工具,调试这段代码了。
代码放在setTimeout里面,总是不太方便。那些运行时间较短的脚本,可能根本来不及打开调试工具。这时就要使用下面的方法。
$ node - -inspect-brk=9229 app.js
上面代码中,--inspect-brk指定在第一行就设置断点。也就是说,一开始运行,就是暂停的状态。


六、忘了写 –inspect 怎么办?
打开调试工具的前提是,启动 Node 脚本时就加上--inspect参数。如果忘了这个参数,还能不能调试呢?
回答是可以的。首先,正常启动脚本。
$ node app.js
然后,在另一个命令行窗口,查找上面脚本的进程号。
$psax|grepapp.js
30464pts/11Sl+:00nodeapp.js
30541pts/12S+:00grepapp.js
上面命令中,app.js的进程号是30464。
接着,运行下面的命令。
$ node -e 'process._debugProcess(30464)'
上面命令会建立进程 30464 与调试工具的连接,然后就可以打开调试工具了。
还有一种方法,就是向脚本进程发送 SIGUSR1 信号,也可以建立调试连接。
$ kill -SIGUSR1 30464


七、参考链接
Debugging Node.js with Google Chrome, by Jacopo Daeli
Debugging Node.js with Chrome DevTools, by Paul Irish
Last minute node debugging, by Remy Sharp

问题汇总:

    1. 侧滑返回如何禁止?

    2. 侧滑出现上一页面 (页面 js 未执行)

    3. 侧滑出现侧拉,页面空白或页面未刷新,(js 未执行)

个人感觉是框架问题,也是机制问题,ios 很容易出现这样的问题,页面禁用侧滑事件无用,出现上一页面,上一页面也检测不到,显示为空白页面

侧拉拉出侧拉的页面,页面内没有检测到任何事件,内部 js 也没有执行!

可以试试 ios 边缘拖拽关闭禁止


ios的边缘拖拽关闭可以通过下列代码来禁止:

plus.webview.currentWebview().setStyle({
                        'popGesture': 'none'
                    });

在软件中,性能一直扮演着重要的角色。在Web应用中,性能变得更加重要,因为如果页面速度很慢的话,用户就会很容易转去访问我们的竞争对手的网站。作为专业的web开发人员,我们必须要考虑这个问题。有很多“古老”的关于性能优化的最佳实践在今天依然可行,例如最小化请求数目,使用CDN以及不编写阻塞页面渲染的代码。然而,随着越来越多的web应用都在使用JavaScript,确保我们的代码运行的很快就变得很重要。

假设你有一个正在工作的函数,但是你怀疑它运行得没有期望的那样快,并且你有一个改善它性能的计划。那怎么去证明这个假设呢?在今天,有什么最佳实践可以用来测试JavaScript函数的性能呢?一般来说,完成这个任务的最佳方式是使用内置的performance.now()函数,来衡量函数运行前和运行后的时间。

在这篇文章中,我们会讨论如何衡量代码运行时间,以及有哪些技术可以避免一些常见的“陷阱”。

Performance.now()

高分辨率时间API提供了一个名为now()的函数,它返回一个DOMHighResTimeStamp对象,这是一个浮点数值,以毫秒级别(精确到千分之一毫秒)显示当前时间。单独这个数值并不会为你的分析带来多少价值,但是两个这样的数值的差值,就可以精确描述过去了多少时间。

这个函数除了比内置的Date对象更加精确以外,它还是“单调”的,简单说,这意味着它不会受操作系统(例如,你笔记本上的操作系统)周期性修改系统时间影响。更简单的说,定义两个Date实例,计算它们的差值,并不代表过去了多少时间。

“单调性”的数学定义是“(一个函数或者数值)以从不减少或者从不增加的方式改变”。

我们可以从另外一种途径来解释它,即想象使用它来在一年中让时钟向前或者向后改变。例如,当你所在国家的时钟都同意略过一个小时,以便最大化利用白天的时间。如果你在时钟修改之前创建了一个Date实例,然后在修改之后创建了另外一个,那么查看这两个实例的差值,看上去可能像“1小时零3秒又123毫秒”。而使用两个performance.now()实例,差值会是“3秒又123毫秒456789之一毫秒”。

在这一节中,我不会涉及这个API的过多细节。如果你想学习更多相关知识或查看更多如何使用它的示例,我建议你阅读这篇文章:Discovering the High Resolution Time API。

既然你知道高分辨率时间API是什么以及如何使用它,那么让我们继续深入看一下它有哪些潜在的缺点。但是在此之前,我们定义一个名为makeHash()的函数,在这篇文章剩余的部分,我们会使用它。

functionmakeHash(source){

varhash=;

if(source.length===)returnhash;

for(vari=;i

varchar=source.charCodeAt(i);

hash=((hash

hash=hash&hash;// Convert to 32bit integer

}

returnhash;

}

我们可以通过下面的代码来衡量这个函数的执行效率:

vart0=performance.now();

varresult=makeHash('Peter');

vart1=performance.now();

console.log('Took',(t1-t0).toFixed(4),'milliseconds to generate:',result);

如果你在浏览器中运行这些代码,你应该看到类似下面的输出:

Took 0.2730 milliseconds to generate: 77005292

这段代码的在线演示如下所示:

记住这个示例后,让我们开始下面的讨论。

缺陷1 – 意外衡量不重要的事情

在上面的示例中,你可以注意到,我们在两次调用performance.now()中间只调用了makeHash()函数,然后将它的值赋给result变量。这给我们提供了函数的执行时间,而没有其他的干扰。我们也可以按照下面的方式来衡量代码的效率:

vart0=performance.now();

console.log(makeHash('Peter'));// bad idea!

vart1=performance.now();

console.log('Took',(t1-t0).toFixed(4),'milliseconds');

这个代码片段的在线演示如下所示:

但是在这种情况下,我们将会测量调用makeHash(‘Peter’)函数花费的时间,以及将结果发送并打印到控制台上花费的时间。我们不知道这两个操作中每个操作具体花费多少时间, 只知道总的时间。而且,发送和打印输出的操作所花费的时间会依赖于所用的浏览器,甚至依赖于当时的上下文。

或许你已经完美的意识到console.log方式是不可以预测的。但是执行多个函数同样是错误的,即使每个函数都不会触发I/O操作。例如:

vart0=performance.now();

varname='Peter';

varresult=makeHash(name.toLowerCase()).toString();

vart1=performance.now();

console.log('Took',(t1-t0).toFixed(4),'milliseconds to generate:',result);

同样,我们不会知道执行时间是怎么分布的。它会是赋值操作、调用toLowerCase()函数或者toString()函数吗?

缺陷 #2 – 只衡量一次

另外一个常见的错误是只衡量一次,然后汇总花费的时间,并以此得出结论。很可能执行不同的次数会得出完全不同的结果。执行时间依赖于很多因素:

编辑器热身的时间(例如,将代码编译成字节码的时间)

主线程可能正忙于其它一些我们没有意识到的事情

你的电脑的CPU可能正忙于一些会拖慢浏览器速度的事情

持续改进的方法是重复执行函数,就像这样:

vart0=performance.now();

for(vari=;i

makeHash('Peter');

}

vart1=performance.now();

console.log('Took',((t1-t0)/10).toFixed(4),'milliseconds to generate');

这个示例的在线演示如下所示:

这种方法的风险在于我们的浏览器的JavaScript引擎可能会使用一些优化措施,这意味着当我们第二次调用函数时,如果输入时相同的,那么JavaScript引擎可能会记住了第一次调用的输出,然后简单的返回这个输出。为了解决这个问题,你可以使用很多不同的输入字符串,而不用重复的使用相同的输入(例如‘Peter’)。显然,使用不同的输入进行测试带来的问题就是我们衡量的函数会花费不同的时间。或许其中一些输入会花费比其它输入更长的执行时间。

缺陷 #3 – 太依赖平均值

在上一节中,我们学习到的一个很好的实践是重复执行一些操作,理想情况下使用不同的输入。然而,我们要记住使用不同的输入带来的问题,即某些输入的执行时间可能会花费所有其它输入的执行时间都长。这样让我们退一步来使用相同的输入。假设我们发送同样的输入十次,每次都打印花费了多长时间。我们会得到像这样的输出:

Took0.2730millisecondstogenerate:77005292

Took0.0234millisecondstogenerate:77005292

Took0.0200millisecondstogenerate:77005292

Took0.0281millisecondstogenerate:77005292

Took0.0162millisecondstogenerate:77005292

Took0.0245millisecondstogenerate:77005292

Took0.0677millisecondstogenerate:77005292

Took0.0289millisecondstogenerate:77005292

Took0.0240millisecondstogenerate:77005292

Took0.0311millisecondstogenerate:77005292

请注意第一次时间和其它九次的时间完全不一样。这很可能是因为浏览器中的JavaScript引擎使用了优化措施,需要一些热身时间。我们基本上没有办法避免这种情况,但是会有一些好的补救措施来阻止我们得出一些错误的结论。

一种方式是去计算后面9次的平均时间。另外一种更加使用的方式是收集所有的结果,然后计算“中位数”。基本上,它会将所有的结果排列起来,对结果进行排序,然后取中间的一个值。这是performance.now()函数如此有用的地方,因为无论你做什么,你都可以得到一个数值。

让我们再试一次,这次我们使用中位数函数:

varnumbers=[];

for(vari=;i

vart0=performance.now();

makeHash('Peter');

vart1=performance.now();

numbers.push(t1-t0);

}

functionmedian(sequence){

sequence.sort();// note that direction doesn't matter

returnsequence[Math.ceil(sequence.length/2)];

}

console.log('Median time',median(numbers).toFixed(4),'milliseconds');

缺陷 #4 – 以可预测的方式比较函数

我们已经理解衡量一些函数很多次并取平均值总会是一个好主意。而且,上面的示例告诉我们使用中位数要比平均值更好。

在实际中,衡量函数执行时间的一个很好的用处是来了解在几个函数中,哪个更快。假设我们有两个函数,它们的输入参数类型一致,输出结果相同,但是它们的内部实现机制不一样。

functionisIn(haystack,needle){

varfound=false;

haystack.forEach(function(element){

if(element.toLowerCase()===needle.toLowerCase()){

found=true;

}

});

returnfound;

}

console.log(isIn(['a','b','c'],'B'));// true

console.log(isIn(['a','b','c'],'d'));// false

我们可以立刻发现这个方法有改进的地方,因为haystack.forEach循环总会遍历所有的元素,即使我们可以很快找到一个匹配的元素。现在让我们使用for循环来编写一个更好的版本。

functionisIn(haystack,needle){

for(vari=,len=haystack.length;i

if(haystack[i].toLowerCase()===needle.toLowerCase()){

returntrue;

}

}

returnfalse;

}

console.log(isIn(['a','b','c'],'B'));// true

console.log(isIn(['a','b','c'],'d'));// false

现在我们来看哪个函数更快一些。我们可以分别运行每个函数10次,然后收集所有的测量结果:

functionisIn1(haystack,needle){

varfound=false;

haystack.forEach(function(element){

if(element.toLowerCase()===needle.toLowerCase()){

found=true;

}

});

returnfound;

}

functionisIn2(haystack,needle){

for(vari=,len=haystack.length;i

if(haystack[i].toLowerCase()===needle.toLowerCase()){

returntrue;

}

}

returnfalse;

}

console.log(isIn1(['a','b','c'],'B'));// true

console.log(isIn1(['a','b','c'],'d'));// false

console.log(isIn2(['a','b','c'],'B'));// true

console.log(isIn2(['a','b','c'],'d'));// false

functionmedian(sequence){

sequence.sort();// note that direction doesn't matter

returnsequence[Math.ceil(sequence.length/2)];

}

functionmeasureFunction(func){

varletters='a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z'.split(',');

varnumbers=[];

for(vari=;i

vart0=performance.now();

func(letters,letters[i]);

vart1=performance.now();

numbers.push(t1-t0);

}

console.log(func.name,'took',median(numbers).toFixed(4));

}

measureFunction(isIn1);

measureFunction(isIn2);

我们运行上面的代码, 可以得出如下的输出:

true

false

true

false

isIn1took0.0050

isIn2took0.0150

这个示例的在线演示如下所示:

到底发生了什么?第一个函数的速度要快3倍!那不是我们假设的情况。

其实假设很简单,但是有些微妙。第一个函数使用了haystack.forEach方法,浏览器的JavaScript引擎会为它提供一些底层的优化,但是当我们使用数据索引技术时,JavaScript引擎没有提供对应的优化。这告诉我们:在真正测试之前,你永远不会知道。

结论

在我们试图解释如何使用performance.now()方法得到JavaScript精确执行时间的过程中,我们偶然发现了一个基准场景,它的运行结果和我们的直觉相反。问题在于,如果你想要编写更快的web应用,我们需要优化JavaScript代码。因为计算机(几乎)是一个活生生的东西,它很难预测,有时会带来“惊喜”,所以如果了解我们代码是否运行更快,最可靠的方式就是编写测试代码并进行比较。

当我们有多种方式来做一件事情时,我们不知道哪种方式运行更快的另一个原因是要考虑上下文。在上一节中,我们执行一个大小写不敏感的字符串查询来寻找1个字符串是否在其它26个字符串中。当我们换一个角度来比较1个字符串是否在其他100,000个字符串中时,结论可能是完全不同的。

上面的列表不是很完整的,因为还有更多的缺陷需要我们去发现。例如,测试不现实的场景或者只在JavaScript引擎上测试。但是确定的是对于JavaScript开发者来说,如果你想编写更好更快的Web应用,performance.now()是一个很棒的方法。最后但并非最不重要,请谨记衡量执行时间只是“更好的代码”的一反面。我们还要考虑内存消耗以及代码复杂度。

three.js 学习

创建场景

    三个要素: 场景,相机,渲染器

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight ); // setSize 
document.body.appendChild( renderer.domElement );

PerspectiveCamera 场景

现在已经设置好了场景,相机,渲染器。 three  有几个不同的渲染器, 我们使用 PerspectiveCamera.

1. 第一个属性是视野, FOV 是任何给定时刻显示器上看到的场景的范围。该值以度为单位。

2. 第二个是设置宽高,

3. 第三个属性与第四个属性是近和远裁剪平面。这意味着,远离相机和物体远远超过或接近近处的物体不会被渲染,

WebGLRenderer 渲染器

 除了 webGLRenderer 之外 还有其他一些功能,通常用作旧版浏览器的用户的回退功能,或者由于某些原因而无法支持 WebGL 的用户。

添加到页面

我们将 renderer 元素添加到 HTML 文档中。这是渲染器用来向我们显示场景的 <canvas> 元素。

setSize 

除了创建渲染器实例外,设置 程序显示的实际大小,如果想保持应用程序的大小,但以较低的分辨率渲染它,可以通过 false 作为 updateStyle (第三个参数)调用 setSize 来实现,例如 setSize(window.innerWidth / 2, window.innerHeight / 2, false)

git 用了好久,但是都比较粗浅,记忆并不深刻,准备好好整理一下

Git 第一步

1. 每次初始化一个厂库,很简单 

git init 

2.  新建一个文件,把文件添加到厂库, 命令 git add

git add 文件名

3. 用命令 git commit 提交到厂库

git commit -m "第一次提交" // -m "这里是提交的描述"

-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。

嫌麻烦不想输入-m "xxx"行不行?确实有办法可以这么干,但是强烈不建议你这么干,因为输入说明对自己对别人阅读都很重要。

git commit 命令执行成功后会告诉你,1个文件被改动(我们新添加的 文件),插入了两行内容( 有两行内容)。

为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件

Git 时光机回退

回到过去

1. 在Git中,用HEAD表示当前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

git reset --hard HEAD^

2. 最新的那个版本 已经看不到了!好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?

 办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊 就可以指定回到未来的某个版本。

git reset --hard 版本号  

版本号没有必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为 Git 可能会找多个版本号,就无法确定是哪一个了

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针。 现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?

在Git中,总是有后悔药可以吃的。当你用git reset --hard HEAD^ 回退到 某个 版本时,再想恢复到 以前版本 ,就必须找到 commit id。Git提供了一个命令git reflog用来记录你的每一次命令。


git reflog

  • HEAD 指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令 git reset --hard commit_id。

  • 穿梭前,用 git log 可以查看提交历史,以便确定要回退到哪个版本。

  • 要重返未来,用 git reflog 查看命令历史,以便确定要回到未来的哪个版本。

微信网页样式经常跑偏一大部分是网页字体问题
;(function () {  
 if (typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function") {
  handleFontSize(); 
 } else { 
 if (document.addEventListener) { 
 document.addEventListener("WeixinJSBridgeReady", handleFontSize, false); 
 } else if (document.attachEvent) {  
document.attachEvent("WeixinJSBridgeReady", handleFontSize); 
 document.attachEvent("onWeixinJSBridgeReady", handleFontSize);  
}  }   function handleFontSize() { 
 // 设置网页字体为默认大小 
 WeixinJSBridge.invoke('setFontSizeCallback', {'fontSize': 0}); 
 // 重写设置网页字体大小的事件  
WeixinJSBridge.on('menu:setfont', function () {  WeixinJSBridge.invoke('setFontSizeCallback', {'fontSize': 0});  });  }
 })()

Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。)

语法:

new Promise( function(resolve, reject) {...} /* executor */  );

参数:

executor 

    executor 是一个带有 resolve 和 reject 两个参数的函数,executor 函数在 Promise 构造函数执行时同步执行,被传递 resolve  和 reject 函数 (executor 函数在 Promise 构造函数返回新建对象前被调用)。resolve 和 reject 函数被调用时,分别将 promise 的状态改为 fulfilled(完成)或 rejected(失败)。executor 内部通常会执行一个异步操作,一旦完成,可以调用 resolve 函数将 promise 状态改成 fulfilled,或者在发生错误是将它的状态改为 rejected

如果在 executor 函数中抛出一个错误,那么该 promise 状态为 rejected 。executor 函数的返回值被忽略。


描述:

    Promise 对象是一个代理对象(代理一个值),被代理的值在 Promise 对象创建时可能是未知的。他允许你为异步操作的成功和失败费别绑定相应的处理方法(handlers)。这让异步方法可以像同步那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的 Promise 对象

一个 Promise 有以下几种状态:

  • pending : 初始状态,成功或者失败状态
  • fulfilled:意味着操作成功完成。
  • rejected:意味着操作失败。

pending 状态的 Promise 对象可能触发 fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任意一种情况出现是, Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当 Promise 状态为 fulfilled 时, 调用 then 的 onfulfilled 方法, 当 Promise 状态为 rejected 时, 调用 then 的 onrejected 方法,所以在异步操作完成和绑定处理方法之间不存在竞争)。

因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回 promise 对象, 所以它们可以被链试调用


不要被迷惑了: 有一些语言中有惰性求值和延时计算的特性,它们也被称为“promises”,例如Scheme. Javascript中的promise代表一种已经发生的状态, 而且可以通过回调方法链在一起。 如果你想要的是表达式的延时计算,考虑无参数的"箭头方法":  f = () =>表达式 创建惰性求值的表达式使用 f() 求值。

注意: 如果一个promise对象处在fulfilled或rejected状态而不是pending状态,那么它也可以被称为settled状态。你可能也会听到一个术语resolved ,它表示promise对象处于fulfilled状态。(此处有待商榷。按本文的原英文翻译resolved表示fulfilled,但是后面的术语链接又表明resolved和fulfilled并不是一样。)关于promise的术语, Domenic Denicola 的 States and fates 有更多详情可供参考。

属性:

Promise.length 

    length 属性,其值总是为1(构造器参数的数目)

Promise.prototype

    表示 Promise 构造器的原型.


方法:

Promise.all(iterable) 

    这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。(可以参考jQuery.when方法---译者注)

Promise.race(iterable)

    当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

Promise.reject(reason)

    返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

Promise.resolve(value)

    返回一个状态由给定value决定的Promise对象。如果该值是一个Promise对象,则直接返回该对象;如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。


创建 Promise

Promise 对象是由关键字 new 及其构造函数来创建的。该构造函数会?把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。

const myFirstPromise = new Promise((resolve, reject) => {
  // ?异步操作,最终调用:
  //
  //   resolve(someValue); // fulfilled
  // ?或
  //   reject("failure reason"); // rejected
});

想要某个函数?拥有promise功能,只需让其返回一个promise即可。

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};



Array.prototype 属性表示 Array 构造函数的原型,并允许您向所有的 Array 对象添加新的属性和方法。

描述

Array 实例继承自 Array.prototype 。 与所有构造函数一样,可以更改构造函数的原型对象,以对所有的 Array 实例进行更改。 例如,可以添加新的方法和属性以扩展所有 Array 对象。 

Array.prototype 本身也是一个 Array

Array.isArray(Array.prototype)

属性

Array.prototype.constructor

     所有的数组实例都继承了这个属性,它的值就是 Array, 表明了所有的数组都是由 Array 构造函数出来的。

Array.prototype.length

    因为 Array.prototype 也是个数组,所以它也有 length 属性, 这个值为0, 因为它是个空数组。

方法

会改变自身的方法

下面这些方法会改变调用它们的对象自身的值:

Array.prototype.copyWithin()

    在数组内部,将一段元素序列拷贝到另一段元素序列上,覆盖原有的值。

Array.prototype.fill()

    将数组中指定区间的所有元素的值,都替换成某个固定的值。

Array.prototype.pop()

    删除数组的最后一个元素,并返回这个元素。

Array.prototype.push()

    在数组的末尾增加一个或多个元素,并返回数组的新长度。

Array.prototype.reverse()

    颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个。

Array.prototype.shift()

    删除数组的第一个元素,并返回这个元素。

Array.prototype.sort()

    对数组元素进行排序,并返回当前数组。

Array.prototype.splice()

    在任意的位置给数组添加或删除任意个元素。

Array.prototype.unshift()

    在数组的开头增加一个或多个元素,并返回数组的新长度。

不会改变自身的方法

下面的这些方法绝对不会改变调用它们的对象的值,只会返回一个新的数组或者返回一个其他的期望值

Array.prototype.concat()

    返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。

Array.prototype.includes()

    判断单签数字是否包含某指定值,如果是返回 true ,否则返回 false。

Array.prototype.join()

    连接所有数组元素组成一个字符串。

Array.prototype.slice()

    抽取当前数组中的一段元素组合成一个新的数组。

Array.prototype.toSource()  // 这个 API 并没有标准化

    返回一个表示当前数组字面量的字符串。遮蔽了原型链上的 Object.prototype.toSource() 方法。

Array.prototype.toString()

    返回一个由所有数组元素组合而成的字符串。遮蔽了原型链上的 Object.prototype.toString() 方法

Array.prototype.toLocaleString()

   返回一个由所有数组元素组合而成的本地化后的字符串。遮蔽了原型链上的 Object.prototype.toLocaleString() 方法。

Array.prototype.indexOf()

    返回数组中第一个与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。

Array.prototype.lastIndexOf()

    返回数组中最后一个(从右边数第一个)与指定值相等的元素的索引,如果找不到这样的元素,则返回 -1。


遍历方法

    在下面的众多遍历方法中,有很多方法都需要指定一个回调函数作为参数。在回调函数执行之前,数组的长度会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做

Array.prototype.forEach()

    为数组中的每个元素执行一次回调函数。

Array.prototype.entries() 

    返回一个数组迭代器对象,该迭代器会包含所有数组元素的键值对。

Array.prototype.every()

    如果数组中的每个元素都满足测试函数,则返回 true,否则返回 false。

Array.prototype.some()

    如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。

Array.prototype.filter()

    将所有在过滤函数中返回 true 的数组元素放进一个新数组中并返回。

Array.prototype.find() 

    找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined。

Array.prototype.findIndex() 

    找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1。

 Array.prototype.keys() 

    返回一个数组迭代器对象,该迭代器会包含所有数组元素的键。Array.prototype.map() 返回一个由回调函数的返回值组成的新数组。

Array.prototype.reduce()

    从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

Array.prototype.reduceRight()

    从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值。

 Array.prototype.values() 

    返回一个数组迭代器对象,该迭代器会包含所有数组元素的值。

Array.prototype[@@iterator]()

    和上面的 values() 方法是同一个函数。