PHP连接sql

PHP连接数据库

连接数据库有俩种方式

1、面向过程:

$link = mysqli_connect("localhost(本地地址)","数据库的账户","","数据库的名字");
if(mysqli_errno($link)){
echo mysqli_error($link);
}
//sql语句
$sql = "INSERT INTO users (username,age,pwd) VALUES ('王五',20,654321)";
//设置编码格式
//方式一:
//mysqli_query($link,"set names utf8");
//方式二
mysqli_set_charset($link, "utf8");
//执行sql
$res = mysqli_query($link, $sql);
if($res){
echo "添加成功";
}else{
echo "添加失败";
}

2、面向对象

$mysqli = new mysqli("localhost","root","","house");
if($mysqli->errno){
echo $mysqli->error;
}else{
echo "连接数据库成功";
}
$name = "马云";
$age  = 38;
$pwd = 123654;
$sql = "INSERT INTO users (username,age,pwd) VALUES ('".$name."',$age,$pwd)";
//echo $sql;die;
$reslut = add($sql);
if($reslut){
echo "添加成功";
}else{
echo "添加失败";
}

数据库的增删改查

下面都是我封装的函数…

1、添加
sql语句
举例:
$sql = “INSERT INTO users(username,pwd,age) VALUES (‘王林’,123123,45)”;

users在你数据库表的名字 username,pwd,age,在你数据库的字段,VALEUS 括号里是你要添加的内容;

function add($sql){
//连接数据库
$link = mysqli_connect("localhost","root","","house");
//设置编码格式
mysqli_query($link, "set names utf8");
$res = mysqli_query($link, $sql);

//    $reslut = mysqli_insert_id($link);
//    echo $reslut;die;
if($res){
    return true;    
}else{
    return false;
}
}

2、删除

$sql = “DELETE FROM users WHERE id=18”;

WHERE后面是你添加的条件为了更准确的去删除;

function delete($sql){
//连接数据库
$link = mysqli_connect("localhost","root","","house");
mysqli_query($link, "set names utf8");
$res = mysqli_query($link, $sql);
if($res){
    return true;    
}else{
    return false;
}
}    

3、更改
$sql = “UPDATE users SET username=’王’ WHERE id=18”;

function update($sql){
//连接数据库
$link = mysqli_connect("localhost","root","","house");
mysqli_query($link, "set names utf8");
$res = mysqli_query($link, $sql);
if($res){
    return true;    
}else{
    return false;
}
}

4、查询
$sql = “SELECT * FROM users”;

function getlist($sql){
//连接数据库
$link = mysqli_connect("localhost","root","","house");
//设置编码格式
mysqli_query($link, "set names utf8");
//执行sql
$res = mysqli_query($link, $sql);
//取所有数据
//    $list = mysqli_fetch_all($res);//常用
//    $arr = array();
//    while($list = mysqli_fetch_row($res)){
//        $arr[] = $list;
//    }
//常用
while($list = mysqli_fetch_assoc($res)){
    $arr[] = $list;
}

    return $arr;
}

CSS的垂直居中和水平居中总结

前言

CSS居中一直是一个比较敏感的话题,为了以后开发的方便,楼主觉得确实需要总结一下了,总的来说,居中问题分为垂直居中和水平居中,实际上水平居中是很简单的,但垂直居中的方式和方法就千奇百怪了

内联元素居中方案

水平居中设置:

1、行内元素 设置 text-align:center;
2、Flex布局 设置display:flex;justify-content:center;(灵活运用)

垂直居中设置:

1、父元素高度确定的单行文本(内联元素) 设置 height = line-height;
2、父元素高度确定的多行文本(内联元素) a:插入 table (插入方法和水平居中一样),然后设置 vertical-align:middle; b:先设置 display:table-cell 再设置 vertical-align:middle;

块级元素居中方案

水平居中设置:

1、定宽块状元素 设置 左右 margin 值为 auto;

2、不定宽块状元素 a:在元素外加入 table 标签(完整的,包括 table、tbody、tr、td),该元素写在 td 内,然后设置 margin 的值为 auto; b:给该元素设置 display:inine 方法; c:父元素设置 position:relative 和 left:50%,子元素设 position:relative 和 left:50%;

垂直居中设置:

1.使用position:absolute(fixed),设置left、top、margin-left、margin-top的属性;

.box{
position:absolute;/*或fixed*/
top:50%;
left:50%;
margin-top:-100px;
margin-left:-200px;
}

3.利用display:table-cell属性使内容垂直居中;

.box{
display:table-cell;
vertical-align:middle;
text-align:center;
width:120px;
height:120px;
background:purple;
}

4.使用css3的新属性transform:translate(x,y)属性;

.box{
position: absolute;
transform: translate(50%,50%);
-webkit-transform:translate(50%,50%);
-moz-transform:translate(50%,50%);
-ms-transform:translate(50%,50%);
}

5.最高大上的一种,使用:before元素;

.box{
position:fixed;
display:block;
background:rgba(0,0,0,.5);
}
.box:before{
content:'';
display:inline-block;
vertical-align:middle;
height:100%;
}
.box.content{
width:60px;
height:60px;
line-height:60px;
color:red;

6.Flex布局;

.box{
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
水平居中
-webkit-box-align: center;
-moz-box-align: center;
-ms-flex-pack:center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
 垂直居中
-webkit-box-pack: center;
-moz-box-pack: center;
-ms-flex-align:center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
}    

over!

前端面试中常见的算术问题

前端面试中的常见的算法问题

虽说我们很多时候前端很少有机会接触到算法。大多都交互性的操作,然而从各大公司面试来看,算法依旧是考察的一方面。实际上学习数据结构与算法对于工程师去理解和分析问题都是有帮助的。如果将来当我们面对较为复杂的问题,这些基础知识的积累可以帮助我们更好的优化解决思路。下面罗列在前端面试中经常撞见的几个问题吧

Q1 判断一个单词是否是回文?

回文是指把相同的词汇或句子,在下文中调换位置或颠倒过来,产生首尾回环的情趣,叫做回文,也叫回环。比如 mamam redivider .

很多人拿到这样的题目非常容易想到用for 将字符串颠倒字母顺序然后匹配就行了。其实重要的考察的就是对于reverse的实现。其实我们可以利用现成的函数,将字符串转换成数组,这个思路很重要,我们可以拥有更多的自由度去进行字符串的一些操作。

function checkPalindrom(str) {  
return str == str.split('').reverse().join('');
}    

Q2 去掉一组整型数组重复的值

比如输入: [1,13,24,11,11,14,1,2] 
输出: [1,13,24,11,14,2]
需要去掉重复的11 和 1 这两个元素。

这道问题出现在诸多的前端面试题中,主要考察个人对Object的使用,利用key来进行筛选。

/**
* unique an array 
**/
let unique = function(arr) {  
let hashTable = {};
let data = [];
for(let i=0,l=arr.length;i<l;i++) {
if(!hashTable[arr[i]]) {
  hashTable[arr[i]] = true;
  data.push(arr[i]);
}
}
return data
}
module.exports = unique;  

Q3 统计一个字符串出现最多的字母

给出一段英文连续的英文字符窜,找出重复出现次数最多的字母

输入 : afjghdfraaaasdenas 
输出 : a

前面出现过去重的算法,这里需要是统计重复次数。

function findMaxDuplicateChar(str) {  
if(str.length == 1) {
return str;
}
let charObj = {};
for(let i=0;i<str.length;i++) {
if(!charObj[str.charAt(i)]) {
  charObj[str.charAt(i)] = 1;
}else{
  charObj[str.charAt(i)] += 1;
}
}
let maxChar = '',
  maxValue = 1;
  for(var k in charObj) {
if(charObj[k] >= maxValue) {
  maxChar = k;
  maxValue = charObj[k];
}
}
return maxChar;
}
module.exports = findMaxDuplicateChar;

Q4 排序算法

如果抽到算法题目的话,应该大多都是比较开放的题目,不限定算法的实现,但是一定要求掌握其中的几种,所以冒泡排序,这种较为基础并且便于理解记忆的算法一定需要熟记于心。冒泡排序算法就是依次比较大小,小的的大的进行位置上的交换。

function bubbleSort(arr) {  
for(let i = 0,l=arr.length;i<l-1;i++) {
    for(let j = i+1;j<l;j++) { 
      if(arr[i]>arr[j]) {
            let tem = arr[i];
            arr[i] = arr[j];
            arr[j] = tem;
        }
    }
}
return arr;
}
module.exports = bubbleSort;

除了冒泡排序外,其实还有很多诸如 插入排序,快速排序,希尔排序等。每一种排序算法都有各自的特点。全部掌握也不需要,但是心底一定要熟悉几种算法。 比如快速排序,其效率很高,而其基本原理如图(来自wiki):

Mou

算法参考某个元素值,将小于它的值,放到左数组中,大于它的值的元素就放到右数组中,然后递归进行上一次左右数组的操作,返回合并的数组就是已经排好顺序的数组了

function quickSort(arr) {

if(arr.length<=1) {
    return arr;
}

let leftArr = [];
let rightArr = [];
let q = arr[0];
for(let i = 1,l=arr.length; i<l; i++) {
    if(arr[i]>q) {
        rightArr.push(arr[i]);
    }else{
        leftArr.push(arr[i]);
    }
}

return [].concat(quickSort(leftArr),[q],quickSort(rightArr));
}
module.exports = quickSort;

安利大家一个学习的地址,通过动画演示算法的实现。

HTML5 Canvas Demo: Sorting Algorithms

Q5 不借助临时变量,进行两个整数的交换

输入 a = 2, b = 4 输出 a = 4, b =2

这种问题非常巧妙,需要大家跳出惯有的思维,利用 a , b进行置换。

主要是利用 + - 去进行运算,类似 a = a + ( b - a) 实际上等同于最后 的 a = b;

function swap(a , b) {  
b = b - a;
a = a + b;
b = a - b;
return [a,b];
}
module.exports = swap;    

Q6 使用canvas 绘制一个有限度的斐波那契数列的曲线

Mou

数列长度限定在9.

斐波那契数列,又称黄金分割数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列主要考察递归的调用。我们一般都知道定义

fibo[i] = fibo[i-1]+fibo[i-2];  

生成斐波那契数组的方法

function getFibonacci(n) {  
var fibarr = [];
var i = 0;
while(i<n) {
if(i<=1) {
  fibarr.push(i);
}else{
  fibarr.push(fibarr[i-1] + fibarr[i-2])
}
i++;
}
return fibarr;
}

剩余的工作就是利用canvas arc方法进行曲线绘制了

Q7 找出下列正数组的最大差值比如:

输入 [10,5,11,7,8,9]
输出 6

这是通过一道题目去测试对于基本的数组的最大值的查找,很明显我们知道,最大差值肯定是一个数组中最大值与最小值的差。

function getMaxProfit(arr) {
var minPrice = arr[0];
var maxProfit = 0;

for (var i = 0; i < arr.length; i++) {
    var currentPrice = arr[i];

    minPrice = Math.min(minPrice, currentPrice);

    var potentialProfit = currentPrice - minPrice;

    maxProfit = Math.max(maxProfit, potentialProfit);
}

return maxProfit;
}    

Q8 随机生成指定长度的字符串

实现一个算法,随机生成指制定长度的字符窜。

比如给定 长度 8  输出 4ldkfg9j


function randomString(n) {  
let str = 'abcdefghijklmnopqrstuvwxyz9876543210';
let tmp = '',
  i = 0,
  l = str.length;
  for (i = 0; i < n; i++) {
tmp += str.charAt(Math.floor(Math.random() * l));
}
return tmp;
}
module.exports = randomString;

Q9 实现类似getElementsByClassName 的功能

自己实现一个函数,查找某个DOM节点下面的包含某个class的所有DOM节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供DOM查找函数。

function queryClassName(node, name) {  
var starts = '(^|[ \n\r\t\f])',
   ends = '([ \n\r\t\f]|$)';
   var array = [],
    regex = new RegExp(starts + name + ends),
    elements = node.getElementsByTagName("*"),
    length = elements.length,
    i = 0,
    element;

while (i < length) {
    element = elements[i];
    if (regex.test(element.className)) {
        array.push(element);
    }

    i += 1;
}

return array;
}

Q10 使用JS 实现二叉查找树(Binary Search Tree)

一般叫全部写完的概率比较少,但是重点考察你对它的理解和一些基本特点的实现。 二叉查找树,也称二叉搜索树、有序二叉树(英语:ordered binary tree)是指一棵空树或者具有下列性质的二叉树:

任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

任意节点的左、右子树也分别为二叉查找树;

没有键值相等的节点。二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低。为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。

Mou

在写的时候需要足够理解二叉搜素树的特点,需要先设定好每个节点的数据结构

class Node {  
constructor(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
}
}

树是有节点构成,由根节点逐渐延生到各个子节点,因此它具备基本的结构就是具备一个根节点,具备添加,查找和删除节点的方法.

    class BinarySearchTree {
    constructor() {
this.root = null;
}
insert(data) {
let n = new Node(data, null, null);
if (!this.root) {
  return this.root = n;
}
let currentNode = this.root;
let parent = null;
while (1) {
  parent = currentNode;
  if (data < currentNode.data) {
    currentNode = currentNode.left;
    if (currentNode === null) {
      parent.left = n;
      break;
    }
  } else {
    currentNode = currentNode.right;
    if (currentNode === null) {
      parent.right = n;
      break;
    }
  }
}
}

remove(data) {
this.root = this.removeNode(this.root, data)
}

removeNode(node, data) {
if (node == null) {
  return null;
}

if (data == node.data) {
  // no children node
  if (node.left == null && node.right == null) {
    return null;
  }
  if (node.left == null) {
    return node.right;
  }
  if (node.right == null) {
    return node.left;
  }

  let getSmallest = function(node) {
    if(node.left === null && node.right == null) {
      return node;
    }
    if(node.left != null) {
      return node.left;
    }
    if(node.right !== null) {
      return getSmallest(node.right);
    }

  }
  let temNode = getSmallest(node.right);
  node.data = temNode.data;
  node.right = this.removeNode(temNode.right,temNode.data);
  return node;

} else if (data < node.data) {
  node.left = this.removeNode(node.left,data);
  return node;
} else {
  node.right = this.removeNode(node.right,data);
  return node;
}
}

find(data) {
var current = this.root;
while (current != null) {
  if (data == current.data) {
    break;
  }
  if (data < current.data) {
    current = current.left;
  } else {
    current = current.right
  }
}
return current.data;
}
}
module.exports = BinarySearchTree;    

正则

正则表达式

1、什么是正则表达式

正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

2、利用图形化工具理解正则表达式

辅助理解正则表达的在线工具 https://regexper.com/ 我们利用这个工具辅助理解,正则表达式。语法没懂表着急,后面会有,这里只是学会用工具帮助我们学习。

手机号正则

/^1[34578][0-9]{9}$/

Mou

注释:以1开头,第二位为3 4 5 7 9 其中一个,以9位(本身1次加重复8次)0-9数字结尾

单词边界

/\bis\b/

Mou

注释: is前后都是单词的边界,比较晦涩难懂?感受下两者的区别,\b 会方道语法部分讲解

Mou

URL分组替换

/http:(\/\/.+.jpg)/

看不懂的不要慌语法部分后面会有介绍,这里只是展示利用可视化的图形帮助我们理解正则表达式,可以回来再看木有关系

Mou

正则表达式中括号用来分组,这个时候我们可以通过用$1来获取 group#1的内容

Mou

说下这个正则的意义,如果网站用了https,网站引用静态资源也必须是https,否则报错。如果写成 // 会自动识别 http 或者 https

日期匹配与分组替换

/^\d{4}[/-]\d{2}[/-]\d{2}$/

Mou

轻松的可以拿到 group#1 #2 #3 的内容,对应 $1 $2 $3

Mou

到现在已经能结合图形化工具看懂正则表达式表达式了,如果想自己写,还要在正则语法上下点功夫

正则表达式语法

1 修饰符 (三个 g i m)

修饰符与其他语法特殊,字面量方法声名的时候放到//后,构造函数声明的时候,作为第二个参数传入。整个正则表达式可以理解为正则表达式规则字符串+修饰符

g:global 执行一个全局匹配
i:ignore case执行一个不区分大小写的匹配
m: multiple lines多行匹配

修饰符可以一起用 const reg =/\bis\b/gim
来说说他们有什么作用
有g和没有g的区别

Mou

没有g只替换了第一个,有g 所有的都换了

Mou

有i和没有i的区别

Mou

有i忽略大小写,没有i严格区分大小写
Mou

元字符

正则表达式由两种基本字符组成:

原义字符
非打印字符
元字符 (* + ? $ ^ . | \ ( ) { } [ ])

原义字符

这个没什么好解释的,我们一直在举例的 /is/ 匹配字符串’is’

\将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,n”匹配字符“n”。“\n”匹配一个换行符。序列“\”匹配“\”而“(”则匹配“(”

字符类取反 [^]

表示不属于此类

Mou
空格也不属于,好多狗🐶

范围类[-]

正则表达式支持一定范围规则比如 [a-z] [A-Z] [0-9] 可以连写[a-z0-9] 如果你只是想匹配-在 范围类最后加-即可。请看实例。

Mou

预定义类

常用为了方便书写

字符        等价类                    含义
.        [^\n\r]                除了回车符和换行符之外的所有字符
\d        [0-9]                数字字符
\D        [^0-9]                非数字字符
\s        [\t\n\x0B\f\r]        空白符
\S        [^\t\n\x0B\f\r]        非空白符
\w        [a-zA-Z_0-9]        单词字符(字母、数字、下划线)
\W        [^a-zA-Z_0-9]        非单词字符

有了这些预定义类,写一些正则就很方便了,比如我们希望匹配一个 ab+数字+任意字符 的字符串,就可以这样写了 /ab\d./
Mou

边界

字符        含义

^        以xx开头
$        以xx结尾
\b        单词边界,指[a-zA-Z_0-9]之外的字符
\B        非单词边界

边界顾名思义即定义匹配的边界条件,上面基本都在前面的例子碰到了,这里演示下\b与\B 的区别

Mou

量词

字符            含义

?        出现零次或一次
*        出现零次或多次(任意次)
+        出现一次或多次(至道一次)
{n}        对应零次或者n次
{n,m}    至少出现n次但不超过m次
{n,}    至少出现n次(+的升级版)
{0,n}    至多出现n次(其实就是{n,m} 方便记忆而已)

如果没有量词,要匹配4位数字这样写就可以/\d\d\d\d/, 如果匹配50位100位呢?那不是要疯掉了?
有了量词,就可以这样写/\d{100}/, 量词的使用我们在手机号中使用过,表现在可视化中就是循环多少次。

凑一个上面都包含的实例/\d?@\d*@\d+@\d{10}@\d{10,20}@\d{10,}@\d{0,10}/

Mou

贪婪与懒惰(非贪婪)

正则表达式默认会匹配贪婪模式,什么是贪婪模式呢?如其名尽可能多的匹配。我们看个例子🌰。

/\d{3,6}/

Mou

Mou

贪婪模式下,匹配的了最多的情况。

与贪婪对应就是懒惰模式,懒惰对应的就是匹配的尽可能少的情况。如何开启懒惰模式? 在量词后面加?。继续上面的例子

/\d{3,6}?/

Mou

如果想知道,正则表达式是如何匹配量词的,请看 进阶正则表达式 文中有介绍,正则是如何回溯的。

分组与反向引用

分组,又称为子表达式。把正则表达式拆分成小表达式。概念枯燥,说个例子为嘛用分组:

不分组 /abc{2}/

Mou

量词仅作用到最后的c

分组 /(abc){2}/

Mou

注意这里 group #1

分组虽然和运算符() 很像,但是分组在正则表达式中,注意理解组的含义。经常有人滥用分组

/^(http|https)/ 真的需要这样么?其实 /^https?/就可以了,你正则写的特别长的时候,会出现一堆没用的结果,看着都头疼吧。

分组往往和反向引用一起使用,别被概念吓到:当一个正则表达式被分组后,每个分组自动被赋予一个组号,一左到右分别是 $1 $2…

再把之前的例子拿出来

/^(\d{4})/-/-$/

Mou

轻松的可以拿到 group#1 #2 #3 的内容,对应 $1 $2 $3

Mou

如果在反向引用中不想捕获年该如何操作? 加上 ?:即可

/^(?:\d{4})/-/-$/

Mou

over

JavaScript创建对象的7种方法

首先解释几个概念:

1、对象 下面例子中所有的Person函数
2、实例/对象实例 通过new Person() or Person()返回的对象,如var person1 = new Person()中的person1
3、原型对象Person.prototype

工厂模式

function Person() {
var o = new Object();
o.name = 'hanmeimei';
o.say = function() {
alert(this.name);
}
return o;
}
var person1 = Person();

优点:完成了返回一个对象的要求。

缺点:

1、无法通过constructor识别对象,以为都是来自Object,无法得知来自Person
2、每次通过Person创建对象的时候,所有的say方法都是一样的,但是却存储了多次,浪费资源。

构造函数模式

function Person() {
this.name = 'hanmeimei';
this.say = function() {
alert(this.name)
}
}
var person1 = new Person();    

优点:

1、通过constructor或者instanceof可以识别对象实例的类别
2、可以通过new 关键字来创建对象实例,更像OO语言中创建对象实例

缺点:

多个实例的say方法都是实现一样的效果,但是却存储了很多次(两个对象实例的say方法是不同的,因为存放的地址不同)

注意:

1、构造函数模式隐试的在最后返回return this 所以在缺少new的情况下,会将属性和方法添加给全局对象,浏览器端就会添加给window对象。
2、也可以根据return this 的特性调用call或者apply指定this。这一点在后面的继承有很大帮助。        

原型模式

function Person() {}
Person.prototype.name = 'hanmeimei';
Person.prototype.say = function() {
alert(this.name);
}
Person.prototype.friends = ['lilei'];
var person1 = new Person();

优点:

1、say方法是共享的了,所有的实例的say方法都指向同一个。

2、可以动态的添加原型对象的方法和属性,并直接反映在对象实例上。    

var person1 = new Person()
Person.prototype.showFriends = function() {
console.log(this.friends)
}
person1.showFriends()  //['lilei']    

缺点:

1、出现引用的情况下会出现问题具体见下面代码:

    var person1 = new Person();
    var person2 = new Person();
    person1.friends.push('xiaoming');
    console.log(person2.friends)  //['lilei', 'xiaoming']

因为js对引用类型的赋值都是将地址存储在变量中,所以person1和person2的friends属性指向的是同一块存储区域。

2、第一次调用say方法或者name属性的时候会搜索两次,第一次是在实例上寻找say方法,没有找到就去原型对象(Person.prototype)上找say方法,找到后就会在实力上添加这些方法or属性。

3、所有的方法都是共享的,没有办法创建实例自己的属性和方法,也没有办法像构造函数那样传递参数。

注意:

1、优点②中存在一个问题就是直接通过对象字面量给Person.prototype进行赋值的时候会导致constructor改变,所以需要手动设置,其次就是通过对象字面量给Person.prototype进行赋值,会无法作用在之前创建的对象实例上
var person1 = new Person()
Person.prototype = {
name: 'hanmeimei2',
setName: function(name){
  this.name = name
  }
  }
  person1.setName()   //Uncaught TypeError: person1.set is not a function(…)

  这是因为对象实例和对象原型直接是通过一个指针链接的,这个指针是一个内部属性[[Prototype]],可以通过__proto__访问。我们通过对象字面量修改了Person.prototype指向的地址,然而对象实例的__proto__,并没有跟着一起更新,所以这就导致,实例还访问着原来的Person.prototype,所以建议不要通过这种方式去改变Person.prototype属性

构造函数和原型组合模式

function Person(name) {
this.name = name
this.friends = ['lilei']
}
Person.prototype.say = function() {
console.log(this.name)
}
var person1 = new Person('hanmeimei')
person1.say() //hanmeimei

优点:

1、解决了原型模式对于引用对象的缺点
2、解决了原型模式没有办法传递参数的缺点
3、解决了构造函数模式不能共享方法的缺点

缺点:

1、和原型模式中注意①一样

动态原型模式

function Person(name) {
this.name = name
if(typeof this.say != 'function') {
Person.prototype.say = function(
alert(this.name)
}
}

优点:

1、可以在初次调用构造函数的时候就完成原型对象的修改
2、修改能体现在所有的实例中
3、缺点:红宝书都说这个方案完美了。。。。

注意:

1、只用检查一个在执行后应该存在的方法或者属性就行了
2、不能用对象字面量修改原型对象

寄生构造函数模式

function Person(name) {
var o = new Object()
o.name = name
o.say = function() {
alert(this.name)
}
return o
}
var peron1 = new Person('hanmeimei')

优点:

1、和工厂模式基本一样,除了多了个new操作符

缺点:

2、和工厂模式一样,不能区分实例的类别

稳妥构造模式

function Person(name) {
var o = new Object()
o.say = function() {
alert(name)
}
}
var person1 = new Person('hanmeimei');
person1.name  // undefined
person1.say() //hanmeimei

优点:

1、安全,那么好像成为了私有变量,只能通过say方法去访问

缺点:

1、不能区分实例的类别

over!!~~~

javascript数据结构--链表

链表就要来的随性的多,简单的理解可以是这样:在内存中,栈和队列(数组)的存在就是一个整体,如果想要对她内部某一个元素进行移除或是添加一个新元素就要动她内部所有的元素,所谓牵一发而动全身;而链表则不一样,每一个元素都是由元素本身数据和指向下一个元素的指针构成,所以添加或是移除某一个元素不需要对链表整体进行操作,只需要改变相关元素的指针指向就可以了

链表在实际生活中的例子也有很多,比如自行车的链条,环环相扣,但添加或是移除某一个环节只需要对症下药,对相关环节进行操作就OK。再比如:火车,火车就是一个链表,每一节车厢就是元素,想要移除或是添加某一节车厢,只需要把连接车厢的链条改变一下就好了。那么,在javascript中又该怎么去实现链表结构呢?

链表的创建

首先我们要创建一个链表类:

function LinkedList(){
//各种属性和方法的声明
}

然后我们需要一种数据结构来保存链表里面的数据:

var Node=function(element){
this.element=element;
this.next=null;}
//Node类表示要添加的元素,他有两个属性,一个是element,表示添加到链表中的具体的值;另一个是next,表示要指向链表中下一个元素的指针。

接下来,我们需要给链表声明一些方法:

·append(element):向链表尾部添加一个新的元素;
·insert(position,element):向链表特定位置插入元素;
·remove(element):从链表移除一项;
·indexOf(element):返回链表中某元素的索引,如果没有返回-1;
·removeAt(position):从特定位置移除一项;
·isEmpty():判断链表是否为空,如果为空返回true,否则返回false;
·size():返回链表包含的元素个数;
·toString():重写继承自Object类的toString()方法,因为我们使用了Node类;

链表的完整代码:

function LinkedList() {
//Node类声明
let Node = function(element){
    this.element = element;
    this.next = null;
};
//初始化链表长度
let length = 0;
//初始化第一个元素
let head = null;
this.append = function(element){
    //初始化添加的Node实例
    let node = new Node(element),
        current;
    if (head === null){
        //第一个Node实例进入链表,之后在这个LinkedList实例中head就不再是null了
        head = node;
    } else {
        current = head;
        //循环链表知道找到最后一项,循环结束current指向链表最后一项元素
        while(current.next){
            current = current.next;
        }
        //找到最后一项元素后,将他的next属性指向新元素node,j建立链接
        current.next = node;
    }
    //更新链表长度
    length++;
};
this.insert = function(position, element){
    //检查是否越界,超过链表长度或是小于0肯定不符合逻辑的
    if (position >= 0 && position <= length){
        let node = new Node(element),
            current = head,
            previous,
            index = 0;
        if (position === 0){
            //在第一个位置添加
            node.next = current;
            head = node;
        } else {
            //循环链表,找到正确位置,循环完毕,previous,current分别是被添加元素的前一个和后一个元素
            while (index++ < position){
                previous = current;
                current = current.next;
            }
            node.next = current;
            previous.next = node;
        }
        //更新链表长度
        length++;
        return true;
    } else {
        return false;
    }
};
this.removeAt = function(position){
    //检查是否越界,超过链表长度或是小于0肯定不符合逻辑的
    if (position > -1 && position < length){
        let current = head,
            previous,
            index = 0;
        //移除第一个元素
        if (position === 0){
            //移除第一项,相当于head=null;
            head = current.next;
        } else {
            //循环链表,找到正确位置,循环完毕,previous,current分别是被添加元素的前一个和后一个元素
            while (index++ < position){
                previous = current;
                current = current.next;
            }
            //链接previous和current的下一个元素,也就是把current移除了
            previous.next = current.next;
        }
        length--;
        return current.element;
    } else {
        return null;
    }
};
this.indexOf = function(element){
    let current = head,
        index = 0;
    //循环链表找到元素位置
    while (current) {
        if (element === current.element) {
            return index;
        }
        index++;
        current = current.next;
    }
    return -1;
};
this.remove = function(element){
    //调用已经声明过的indexOf和removeAt方法
    let index = this.indexOf(element);
    return this.removeAt(index);
};
this.isEmpty = function() {
    return length === 0;
};
this.size = function() {
    return length;
};
this.getHead = function(){
    return head;
};
this.toString = function(){
    let current = head,
        string = '';
    while (current) {
        string += current.element + (current.next ? ', ' : '');
        current = current.next;
    }
    return string;
};
this.print = function(){
    console.log(this.toString());
};
}

//一个实例化后的链表,里面是添加的数个Node类的实例

ES6版本:

let LinkedList2 = (function () {
class Node {
    constructor(element){
        this.element = element;
        this.next = null;
    }
}
//这里我们使用WeakMap对象来记录长度状态
const length = new WeakMap();
const head = new WeakMap();
class LinkedList2 {
    constructor () {
        length.set(this, 0);
        head.set(this, null);
    }
    append(element) {
        let node = new Node(element),
            current;
        if (this.getHead() === null) {
            head.set(this, node);
        } else {
            current = this.getHead();
            while (current.next) {
                current = current.next;
            }
            current.next = node;
        }
        let l = this.size();
        l++;
        length.set(this, l);
    }
    insert(position, element) {
        if (position >= 0 && position <= this.size()) {

            let node = new Node(element),
                current = this.getHead(),
                previous,
                index = 0;
            if (position === 0) {
                node.next = current;
                head.set(this, node);
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                node.next = current;
                previous.next = node;
            }
            let l = this.size();
            l++;
            length.set(this, l);
            return true;
        } else {
            return false;
        }
    }
    removeAt(position) {
        if (position > -1 && position < this.size()) {
            let current = this.getHead(),
                previous,
                index = 0;
            if (position === 0) {
                head.set(this, current.next);
            } else {
                while (index++ < position) {
                    previous = current;
                    current = current.next;
                }
                previous.next = current.next;
            }
            let l = this.size();
            l--;
            length.set(this, l);
            return current.element;
        } else {
            return null;
        }
    }
    remove(element) {
        let index = this.indexOf(element);
        return this.removeAt(index);
    }
    indexOf(element) {
        let current = this.getHead(),
            index = 0;
        while (current) {
            if (element === current.element) {
                return index;
            }
            index++;
            current = current.next;
        }
        return -1;
    }
    isEmpty() {
        return this.size() === 0;
    }
    size() {
        return length.get(this);
    }
    getHead() {
        return head.get(this);
    }
    toString() {
        let current = this.getHead(),
            string = '';
        while (current) {
            string += current.element + (current.next ? ', ' : '');
            current = current.next;
        }
        return string;

    }
    print() {
        console.log(this.toString());
    }
}
return LinkedList2;
})();
双向链表
function DoublyLinkedList() {
let Node = function(element){
    this.element = element;
    this.next = null;
    this.prev = null; //NEW
};
let length = 0;
let head = null;
let tail = null; //NEW
this.append = function(element){
    let node = new Node(element),
        current;
    if (head === null){
        head = node;
        tail = node; //NEW
    } else {
        //NEW
        tail.next = node;
        node.prev = tail;
        tail = node;
    }
    length++;
};
this.insert = function(position, element){
    if (position >= 0 && position <= length){
        let node = new Node(element),
            current = head,
            previous,
            index = 0;
        if (position === 0){
            if (!head){       //NEW
                head = node;
                tail = node;
            } else {
                node.next = current;
                current.prev = node; //NEW
                head = node;
            }
        } else  if (position === length) { ////NEW
            current = tail;   
            current.next = node;
            node.prev = current;
            tail = node;
        } else {
            while (index++ < position){
                previous = current;
                current = current.next;
            }
            node.next = current;
            previous.next = node;
            current.prev = node; //NEW
            node.prev = previous; //NEW
        }
        length++;
        return true;
    } else {
        return false;
    }
};
this.removeAt = function(position){
    if (position > -1 && position < length){
        let current = head,
            previous,
            index = 0;
        if (position === 0){ //NEW
            if (length === 1){ //
                tail = null;
            } else {
                head.prev = null;
            }
        } else if (position === length-1){  //NEW
            current = tail;
            tail = current.prev;
            tail.next = null;
        } else {
            while (index++ < position){
                previous = current;
                current = current.next;
            }
            previous.next = current.next;
            current.next.prev = previous; //NEW
        }
        length--;
        return current.element;
    } else {
        return null;
    }
};
this.remove = function(element){
    let index = this.indexOf(element);
    return this.removeAt(index);
};
this.indexOf = function(element){
    let current = head,
        index = -1;
    if (element == current.element){
        return 0;
    }
    index++;
    while(current.next){
        if (element == current.element){
            return index;
        }
        current = current.next;
        index++;
    }
    //check last item
    if (element == current.element){
        return index;
    }
    return -1;
};
this.isEmpty = function() {
    return length === 0;
};
this. size = function() {
    return length;
};
this.toString = function(){
    let current = head,
        s = current ? current.element : '';
    while(current && current.next){
        current = current.next;
        s += ', ' + current.element;
    }
    return s;
};
this.inverseToString = function() {
    let current = tail,
        s = current ? current.element : '';
    while(current && current.prev){
        current = current.prev;
        s += ', ' + current.element;
    }
    return s;
};
this.print = function(){
    console.log(this.toString());
};
this.printInverse = function(){
    console.log(this.inverseToString());
};
this.getHead = function(){
    return head;
};
this.getTail = function(){
    return tail;
}
}    

双向链表和单项比起来就是Node类多了一个prev属性,也就是每一个node不仅仅有一个指向它后面元素的指针也有一个指向它前面的指针。

循环链表

明白了前面的基础链表和双向链表之后这个肯定不在话下了,循环,其实就是整个链表实例变成了一个圈,在单项链表中最后一个元素的next属性为null,现在让它指向第一个元素也就是head,那么他就成了单向循环链表。在双向链表中最后一个元素的next属性为null,现在让它指向第一个元素也就是head,那么他就成了双向循环链表。就那么回事..

this

global this

· 在浏览器,在全局范围内,this等价于window对象.

1 <script type="text/javascript">
2 console.log(this === window); //true
3</script>

· 在浏览器里,在全局范围内,用var声明一个变量和给this或者window添加属性是等价的

1 <script type="text/javascript">
2     var foo = "bar";
3     console.log(this.foo); //logs "bar"
4     console.log(window.foo); //logs "bar"
5 </script>

· 如果你在声明一个变量的时候没有使用var或者let(ECMAScript 6),你就是在给全局的this添加或者改变属性值。

1 <script type="text/javascript">
2     foo = "bar";
3
4     function testThis() {
5       foo = "foo";
6     }
7
8     console.log(this.foo); //logs "bar"
9     testThis();
10     console.log(this.foo); //logs "foo"
11 </script>

· 在node环境里,如果使用REPL(Read-Eval-Print Loop,简称REPL:读取-求值-输出,是一个简单的,交互式的编程环境)来执行程序,this并不是最高级的命名空间,最高级的是global.

> this
{ ArrayBuffer: [Function: ArrayBuffer],
Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
...
> global === this
true

· 在node环境里,如果执行一个js脚本,在全局范围内,this以一个空对象开始作为最高级的命名空间,这个时候,它和global不是等价的。

1 test.js脚本内容:
2
3 console.log(this);
4 console.log(this === global);
5 
6 REPL运行脚本:
7 
8 $ node test.js
9 {}
10 false

· 在node环境里,在全局范围内,如果你用REPL执行一个脚本文件,用var声明一个变量并不会和在浏览器里面一样将这个变量添加给this。

1 test.js:
2 
3 var foo = "bar";
4 console.log(this.foo);
5 
6 $ node test.js
7 undefined    

· 但是如果你不是用REPL执行脚本文件,而是直接执行代码,结果和在浏览器里面是一样的(神坑)

1 > var foo = "bar";
2 > this.foo
3 bar
4 > global.foo
5 bar

· 在node环境里,用REPL运行脚本文件的时候,如果在声明变量的时候没有使用var或者let,这个变量会自动添加到global对象,但是不会自动添加给this对象。如果是直接执行代码,则会同时添加给global和this

1 test.js
2 
3 foo = "bar";
4 console.log(this.foo);
5 console.log(global.foo);
6 
7 $ node test.js
8 undefined
9 bar


上面的八种情况可能大家已经绕晕了,总结起来就是:在浏览器里面this是老大,它等价于window对象,如果你声明一些全局变量(不管在任何地方),这些变量都会作为this的属性。在node里面,有两种执行JavaScript代码的方式,一种是直接执行写好的JavaScript文件,另外一种是直接在里面执行一行行代码。对于直接运行一行行JavaScript代码的方式,global才是老大,this和它是等价的。在这种情况下,和浏览器比较相似,也就是声明一些全局变量会自动添加给老大global,顺带也会添加给this。但是在node里面直接脚本文件就不一样了,你声明的全局变量不会自动添加到this,但是会添加到global对象。所以相同点是,在全局范围内,全局变量终究是属于老大的。

function this

· 无论是在浏览器环境还是node环境, 除了在DOM事件处理程序里或者给出了thisArg(接下来会讲到)外,如果不是用new调用,在函数里面使用this都是指代全局范围的this。

1 <script type="text/javascript">
2     foo = "bar";
3 
4     function testThis() {
5       this.foo = "foo";
6     }
7 
8     console.log(this.foo); //logs "bar"
9     testThis();
10     console.log(this.foo); //logs "foo"
11 </script>

test.js

foo = "bar";

function testThis () {
this.foo = "foo";
}
console.log(global.foo);
testThis();console.log(global.foo);
$ node test.js
bar
foo    

· 除非你使用严格模式,这时候this就会变成undefined。

1 <script type="text/javascript">
2     foo = "bar";
3 
4     function testThis() {
5       "use strict";
6       this.foo = "foo";
7     }
8 
9     console.log(this.foo); //logs "bar"
10     testThis();  //Uncaught TypeError: Cannot set property 'foo' of undefined 
11 </script>

· 如果你在调用函数的时候在前面使用了new,this就会变成一个新的值,和global的this脱离干系.

1 <script type="text/javascript">
2     foo = "bar";
3 
4     function testThis() {
5       this.foo = "foo";
6     }
7 
8     console.log(this.foo); //logs "bar"
9     new testThis();
10     console.log(this.foo); //logs "bar"
11 
12     console.log(new testThis().foo); //logs "foo"
13 </script>

我更喜欢把新的值称作一个实例。

函数里面的this其实相对比较好理解,如果我们在一个函数里面使用this,需要注意的就是我们调用函数的方式,如果是正常的方式调用函数,this指代全局的this,如果我们加一个new,这个函数就变成了一个构造函数,我们就创建了一个实例,this指代这个实例,这个和其他面向对象的语言很像。另外,写JavaScript很常做的一件事就是绑定事件处理程序,也就是诸如button.addEventListener(‘click’, fn, false)之类的,如果在fn里面需要使用this,this指代事件处理程序对应的对象,也就是button。

Protoype this

你创建的每一个函数都是函数对象。它们会自动获得一个特殊的属性prototype,你可以给这个属性赋值。当你用new的方式调用一个函数的时候,你就能通过this访问你给prototype赋的值了。

1 function Thing() {
2       console.log(this.foo);
3 }
4 
5 Thing.prototype.foo = "bar";
6 
7 var thing = new Thing(); //logs "bar"
8 console.log(thing.foo);  //logs "bar"

当你使用new为你的函数创建多个实例的时候,这些实例会共享你给prototype设定的值。对于下面的例子,当你调用this.foo的时候,都会返回相同的值,除非你在某个实例里面重写了自己的this.foo

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5     console.log(this.foo);
6 }
7 Thing.prototype.setFoo = function (newFoo) {
8     this.foo = newFoo;
9 }
10 
11 var thing1 = new Thing();
12 var thing2 = new Thing();
13 
14 thing1.logFoo(); //logs "bar"
15 thing2.logFoo(); //logs "bar"
16 
17 thing1.setFoo("foo");
18 thing1.logFoo(); //logs "foo";
19 thing2.logFoo(); //logs "bar";
20 
21 thing2.foo = "foobar";
22 thing1.logFoo(); //logs "foo";
23 thing2.logFoo(); //logs "foobar";

实例里面的this是一个特殊的对象。你可以把this想成一种获取prototype的值的一种方式。当你在一个实例里面直接给this添加属性的时候,会隐藏prototype中与之同名的属性。如果你想访问prototype中的这个属性值而不是你自己设定的属性值,你可以通过在实例里面删除你自己添加的属性的方式来实现

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5     console.log(this.foo);
6 }
7 Thing.prototype.setFoo = function (newFoo) {
8     this.foo = newFoo;
9 }
10 Thing.prototype.deleteFoo = function () {
11     delete this.foo;
12 }
13 var thing = new Thing();
14 thing.setFoo("foo");
15 thing.logFoo(); //logs "foo";
16 thing.deleteFoo();
17 thing.logFoo(); //logs "bar";
18 thing.foo = "foobar";
19 thing.logFoo(); //logs "foobar";
20 delete thing.foo;
21 thing.logFoo(); //logs "bar";

或者你也能直接通过引用函数对象的prototype 来获得你需要的值

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5     console.log(this.foo, Thing.prototype.foo);
6 }
7 
8 var thing = new Thing();
9 thing.foo = "foo";
10 thing.logFoo(); //logs "foo bar";

给一个函数的prototype赋值一个Array通常是一个错误的做法。如果你想每一个实例有他们专属的Array,你应该在函数里面创建而不是在prototype里面创建。

1 function Thing() {
2     this.things = [];
3 }
4 
5 
6 var thing1 = new Thing();
7 var thing2 = new Thing();
8 thing1.things.push("foo");
9 console.log(thing1.things); //logs ["foo"]
10 console.log(thing2.things); //logs []

实际上你可以通过把多个函数的prototype链接起来的从而形成一个原型链,因此this就会魔法般地沿着这条原型链往上查找直到找你你需要引用的值。

1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4 
5 function Thing2() {
6 }
7 Thing2.prototype = new Thing1();
8 
9 
10 var thing = new Thing2();
11 console.log(thing.foo); //logs "bar"          

一些人利用原型链的特性来在JavaScript模仿经典的面向对象的继承方式。任何给用于构建原型链的函数的this的赋值的语句都会隐藏原型链上游的相同的属性

1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4 
5 function Thing2() {
6     this.foo = "foo";
7 }
8 Thing2.prototype = new Thing1();
9 
10 function Thing3() {
11 }
12 Thing3.prototype = new Thing2();
13 
14 
15 var thing = new Thing3();
16 console.log(thing.foo); //logs "foo"

我喜欢把被赋值给prototype的函数叫做方法。在上面的例子中,我已经使用过方法了,如logFoo。这些方法有着相同的prototype,即创建这些实力的原始函数。我通常把这些原始函数叫做构造函数。在prototype里面定义的方法里面使用this会影响到当前实例的原型链的上游的this。这意味着你直接给this赋值的时候,隐藏了原型链上游的相同的属性值。这个实例的任何方法都会使用这个最新的值而不是原型里面定义的这个相同的值。

1 function Thing1() {
2 }
3 Thing1.prototype.foo = "bar";
4 Thing1.prototype.logFoo = function () {
5     console.log(this.foo);
6 }
7 
8 function Thing2() {
9     this.foo = "foo";
10 }
11 Thing2.prototype = new Thing1();
12 
13 
14 var thing = new Thing2();
15 thing.logFoo(); //logs "foo";

在JavaScript里面你可以嵌套函数,也就是你可以在函数里面定义函数。嵌套函数可以通过闭包捕获父函数的变量,但是这个函数没有继承this

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5     var info = "attempting to log this.foo:";
6     function doIt() {
7         console.log(info, this.foo);
8     }
9     doIt();
10 }
11 
12 
13 var thing = new Thing();
14 thing.logFoo();  //logs "attempting to log this.foo: undefined"

在doIt里面的this是global对象或者在严格模式下面是undefined。这是造成很多不熟悉JavaScript的人深陷 this陷阱的根源。在这种情况下事情变得非常糟糕,就像你把一个实例的方法当作一个值,把这个值当作函数参数传递给另外一个函数但是却不把这个实例传递给这个函数一样。在这种情况下,一个方法里面的环境变成了全局范围,或者在严格模式下面的undefined。

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {  
5     console.log(this.foo);   
6 }
7 
8 function doIt(method) {
9     method();
10 }
11 
12 
13 var thing = new Thing();
14 thing.logFoo(); //logs "bar"
15 doIt(thing.logFoo); //logs undefined

你可以通过bind将实例和方法一切传递给函数来解决这个问题,bind是一个函数定义在所有函数和方法的函数对象上面

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () { 
5     console.log(this.foo);
6 }
7 
8 function doIt(method) {
9     method();
10 }
11 
12 
13 var thing = new Thing();
14 doIt(thing.logFoo.bind(thing)); //logs bar

你同样可以使用apply和call来在新的上下文中调用方法或函数。

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () { 
5     function doIt() {
6         console.log(this.foo)
7     }
8     doIt.apply(this);
9 }
10 
11 function doItIndirectly(method) {
12     method();
13 }
14 
15 
16 var thing = new Thing();
17 doItIndirectly(thing.logFoo.bind(thing)); //logs bar

你可以用bind来代替任何一个函数或者方法的this,即便它没有赋值给实例的初始prototype。

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 
5 
6 function logFoo(aStr) {
7     console.log(aStr, this.foo);
8 }
9 
10 
11 var thing = new Thing();
12 logFoo.bind(thing)("using bind"); //logs "using bind bar"
13 logFoo.apply(thing, ["using apply"]); //logs "using apply bar"
14 logFoo.call(thing, "using call"); //logs "using call bar"
15 logFoo("using nothing"); //logs "using nothing undefined"    

你应该避免在构造函数里面返回任何东西,因为这可能代替本来应该返回的实例.

1 function Thing() {
2     return {};
3 }
4 Thing.prototype.foo = "bar";
5 
6 
7 Thing.prototype.logFoo = function () {
8     console.log(this.foo);
9 }
10 
11 
12 var thing = new Thing();
13 thing.logFoo(); //Uncaught TypeError: undefined is not a function

奇怪的是,如果你在构造函数里面返回了一个原始值,上面所述的情况并不会发生并且返回语句被忽略了。最好不要在你将通过new调用的构造函数里面返回任何类型的数据,即便你知道自己正在做什么。如果你想创建一个工厂模式,通过一个函数来创建一个实例,这个时候不要使用new来调用函数。当然这个建议是可选的。
你可以通过使用Object.create来避免使用new,这样同样能够创建一个实例。

1 function Thing() {
2 }
3 Thing.prototype.foo = "bar";
4 
5 
6 Thing.prototype.logFoo = function () {
7     console.log(this.foo);
8 }
9 
10 
11 var thing =  Object.create(Thing.prototype);
12 thing.logFoo(); //logs "bar"     

在这种情况下并不会调用构造函数

1 function Thing() {
2     this.foo = "foo";
3 }
4 Thing.prototype.foo = "bar";
5 
6 
7 Thing.prototype.logFoo = function () {
8     console.log(this.foo);
9 }
10 
11 
12 var thing =  Object.create(Thing.prototype);
13 thing.logFoo(); //logs "bar"    

因为Object.create不会调用构造函数的特性在你继承模式下你想通过原型链重写构造函数的时候非常有用。

1 function Thing1() {
2     this.foo = "foo";
3 }
4 Thing1.prototype.foo = "bar";
5 
6 function Thing2() {
7     this.logFoo(); //logs "bar"
8     Thing1.apply(this);
9     this.logFoo(); //logs "foo"
10 }
11 Thing2.prototype = Object.create(Thing1.prototype);
12 Thing2.prototype.logFoo = function () {
13     console.log(this.foo);
14 }
15 
16 var thing = new Thing2();

DOM event this

在一个HTML DOM事件处理程序里面,this始终指向这个处理程序被所绑定到的HTML DOM节点

1 function Listener()     {
2 document.getElementById("foo").addEventListener("click",
3        this.handleClick);
4 }
5 Listener.prototype.handleClick = function (event) {
6     console.log(this); //logs "<div id="foo"></div>"
7 }
8 
9 var listener = new Listener();
10 document.getElementById("foo").click();

除非你自己通过bind切换了上下文

1 function Listener() {
2     document.getElementById("foo").addEventListener("click", 
3         this.handleClick.bind(this));
4 }
5 Listener.prototype.handleClick = function (event) {
6     console.log(this); //logs Listener {handleClick: function}
7 }
8 
9 var listener = new Listener();
10 document.getElementById("foo").click();

HTML this

在HTML节点的属性里面,你可以放置JavaScript代码,this指向了这个元素

1 <div id="foo" onclick="console.log(this);"></div>
2 <script type="text/javascript">
3 document.getElementById("foo").click(); //logs <div id="foo"...
4 </script>

override this

你不能重写this,因为它是保留字。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
eval("console.log(this.foo)"); //logs "bar"
}
var thing = new Thing();
thing.logFoo();    

with this

你可以通过with来将this添加到当前的执行环境,并且读写this的属性的时候不需要通过this

1 function Thing () {
2 }
3 Thing.prototype.foo = "bar";
4 Thing.prototype.logFoo = function () {
5     with (this) {
6         console.log(foo);
7         foo = "foo";
8     }
9 }
10 
11 var thing = new Thing();
12 thing.logFoo(); // logs "bar"
13 console.log(thing.foo); // logs "foo"    

jQuery this

和HTML DOM元素节点的事件处理程序一样,在许多情况下JQuery的this都指向HTML元素节点。这在事件处理程序和一些方便的方法中都是管用的,比如$.each

1 <div class="foo bar1"></div>
2 <div class="foo bar2"></div>
3 <script type="text/javascript">
4 $(".foo").each(function () {
5     console.log(this); //logs <div class="foo...
6 });
7 $(".foo").on("click", function () {
8     console.log(this); //logs <div class="foo...
9 });
10 $(".foo").each(function () {
11     this.click();
12 });
13 </script>

thisArg this

如果你用过underscore.js 或者 lo-dash 你可能知道许多类库的方法可以通过一个叫做thisArg 的函数参数来传递实例,这个函数参数会作为this的上下文。举个例子,这适用于_.each。原生的JavaScript在ECMAScript 5的时候也允许函数传递一个thisArg参数了,比如forEach。事实上,之前阐述的bind,apply和call的使用已经给你创造了传递thisArg参数给函数的机会。这个参数将this绑定为你所传递的对象。

1 function Thing(type) {
2     this.type = type;
3 }
4 Thing.prototype.log = function (thing) {
5     console.log(this.type, thing);
6 }
7 Thing.prototype.logThings = function (arr) {
8    arr.forEach(this.log, this); // logs "fruit apples..."
9    _.each(arr, this.log, this); //logs "fruit apples..."
10 }
11 
12 var thing = new Thing("fruit");
13 thing.logThings(["apples", "oranges",
"strawberries", "bananas"]);         

boke

gulp详细入门教程

最近使用gulp自动化构建工具来开发网站,在此给大家分享下使用gulp的一些使用教程。

一 gulp安装

1、安装Nodejs

1.1、说明:gulp是基于nodejs,理所当然需要安装Nodejs
1.2、安装:打开nodejs官网,点击绿色下载按钮,它会根据系统信息选择对应(.msi文件).

2、全局安装gulp

2、1    说明:说明全局安装gulp目的是为了通过她只想gulp任务;
2、2安装:命令提示符执行npm install gulp-g;
2、3查看是否正确安装:命令提示符执行gulp-v,出现版本号为安装成功

3、新建package.json文件

3、1 说明:package.json是基于nodejs项目必不可少的配置文件,它是存放在项目根目录的普通json文件;
3、2 执行命令提示符执行npm init进行手动安装
3.3 安装完成后回在当前文件夹下看到如下package.json

Mou
文件说明:

"name":"test",//项目名称(必须)
"version":1.0.0,//项目版本(必须)
"description":"This is for study gulp             “project !",//项目描述(必须)
"devDependencies":{//项目依赖的插件
3.4、查看package.json帮助文档,命令提示符执行npm help package.json

4、安装本地gulp及其常用插件

4.1、执行命令行npm install gulo 安装本地gulp
4.2、安装gulo插件:以gulp-sass为例:
    执行名npm install gulp-sass即可安装gulp-sass插件,安装完成后即可在node_modules文件夹下查看到新安装的插件

5、新建gulpfile.js文件(必须)

5.1、说明:gulpfile.js是gulp项目目的配置文件,是位于项目根目录的普通js文件(其实将gulp.js放入其他文件夹下也是可以的)
5.2、gulpfile.js文件用法(以gulp-sass为例)
    5.2.1    导入工具包require('node_modules里对应模块')
        var gulp=require('gulp')
        var less=require('gulp-less);
    5.2、定义一个testLess任务(自定义任务名称)
        gulp.task('testLess',function(){
        gulp.src('src/less/index.less')//该任务针对的文件
        .pipe(less())//该任务调用的模块
        .pipe(gulp.dest('src/css'));/将会在src/css下生成index.css
        })
        gulp.task('default',['watch'],function(){
        //定义默认任务 并让gulp监视文件变化自动执行
        gulp.watch('sass/*.scss',['sass'])
        })

二、gulp常用插件

1、gulp-uglify(js压缩)
安装:npm install –save-dev gulp-uglify

gulpfile.js代码如下:

var gulp = require('gulp'),
var rename= require('gulp-rename')
var uglify= require("gulp-uglify");
gulp.task('rename',function() {
gulp.src('src/**/*.js')
.pipe(uglify())//压缩
.pipe(rename('index.min.js'))    
.pipe(gulp.dest('build/js'));});
gulp.task('default',['rename']);    
uglify= require("gulp-uglify");

2、gulp-minify-html(html压缩)
安装:npm install –save-dev gulp-minify-html

代码如下:

var gulp = require('gulp'),
var minifyHtml= require("gulp-minify-html");
gulp.task('minify-html',function() {
gulp.src('src/**/*.html')//要压缩的html文件
 .pipe(minifyHtml())//压缩
 .pipe(gulp.dest('build'));});
 gulp.task('default',['minify-html']);

3、 gulp-concat (js文件合并)

安装:npm install –save-dev gulp-concat

代码如下:

var gulp = require('gulp'),
concat= require("gulp-concat");
gulp.task('concat',function() {
gulp.src('src/**/*.js')  //要合并的文件
.pipe(concat('index.js'))  //合并匹配到的js文件并命名为 "index.js"
 .pipe(gulp.dest('build/js'));});
 gulp.task('default',['concat']);

4、gulp-jada

安装:npm install –save-dev gulp-jada

Gulpfile.js代码如下:

var gulp= require('gulp');
var jade= require('gulp-jade');
gulp.task("jade",function(){
gulp.src("./jade/*.jade")
 .pipe(jade({
 pretty:true
 }))
  .pipe(gulp.dest("html/"))
  })
   gulp.task("default",["watch"],function(){
    gulp.watch("jade/*.jade",["jade"]);
    })

5、gulp-less

安装:npm install –save-dev gulp-less

Gulpfile.js代码如下:

var gulp = require('gulp'),
var less= require("gulp-less");
gulp.task('compile-less',function() {
 gulp.src('src/less/*.less')
  .pipe(less())
   .pipe(gulp.dest('build/css'));
   });
   gulp.task('default',['compile-less']);

6、gulp-sass

安装:npm install –save-dev gulp-sass

代码如下:

var gulp = require('gulp'),
var sass= require("gulp-sass");
gulp.task('compile-sass',function() {
 gulp.src('src/sass/*.sass')
 .pipe(sass())
  .pipe(gulp.dest('build/css'));
  });
  gulp.task('default',['compile-sass']);

7、gulp-imagemin(图片压缩

安装:npm install –save-dev gulp-imagemin

代码如下:

var gulp = require('gulp');
var imagemin = require('gulp-imagemin');
gulp.task('uglify-imagemin',function()    {returngulp.src('src/images/*')
.pipe(imagemin())
 .pipe(gulp.dest('build/images'));
 });
 gulp.task('default',['uglify-imagemin']);

8、理解Browserify

browserify是一个使用node支持的CommonJS模块标准 来为浏览器编译模块的,可以解决模块及依赖管理;

先来看看使用gulp常见的问题:

1、 使用 gulp 过程中,偶尔会遇到 Streaming not supported 这样的错误。这通常是因为常规流与 vinyl 文件对象流有差异、
gulp 插件默认使用了只支持 buffer (不支持 stream)的库。比如,不能把 Node 常规流直接传递给 gulp 及其插件。

比如如下代码:会抛出异常的;

vargulp = require('gulp');
varuglify = require('gulp-uglify');
varconcat = require('gulp-concat');
varrename = require('gulp-rename');varfs = require('fs');
gulp.task('bundle',function() {returnfs.createReadStream('./test.txt')
.pipe(uglify())
.pipe(rename('bundle.min.js'))
 .pipe(gulp.dest('dist/'));
 });
 gulp.task('default',['bundle']);

希望能帮助到你们~~