javascript 的值和引用 函数与对象(function object)

javascript 只有6种基本类型

数据类型含义值/引用备注
undefined未定义没有值
number数字 
boolean布尔 
string字符串字符串在赋值运算中会按照引用的方式来处理
function方法引用 
object对象引用 

一般表达式运算结果总是值

函数/方法的返回值可以是值或者引用

两个引用之间如果等值(==),则一定全等(===)

值与引用, 值与值 之间, 即使值相等(==), 也不一定全等(===)

[==在比较的时候可以转换数据类型,===严格比较,只要类型不匹配就返回flase]

然后我们来看函数和对象, javascript是一种基于对象的语言,所有对象的原型(prototype)都是Object

instanceof是一个二元运算符,如:A instanceof B. 其中,A必须是一个合法的JavaScript对象,B必须是一个合法的JavaScript函数 (function)

如果函数B在对象A的原型链 (prototype chain) 中被发现,那么instanceof操作符将返回true,否则返回false.

换句话说就是 就是测试  对象A的__proto__ 属性是否指向函数B的prototype链 ( 对象A是由函数B创建的 )

每个函数默认都会有一个属性叫prototype,

每一个通过函数和new操作符生成的对象都具有一个属性__proto__ 这个属性保存了创建它的构造函数的prototype属性的引用

结合上图就是 f1 和 f2 是由Foo 方法创建的, o1 和 o2 是由 Object方法创建的

所以

console.log(obj instanceof Object)//true

console.log(fn instanceof Function)//true

然后尝试下面的代码

function fn(){}

var obj = {}

console.log(fn instanceof Object)//true

console.log(obj instanceof Function)//false

console.log(Function instanceof  Object); // true

console.log(Object instanceof Function);  // true

fn是由Foo方法创建的,然后Foo.prototype指向Object.prototype, 所以 fn instanceof Object为true

obj是由Object方法创建的,但是Object.prototype并没有指向Function.prototype, 所以返回是false

Function对象的__proto__指向了Object(方法).prototype, 也就是说Function对象是由Object方法创建的,所以返回的是true

后面两个麻烦一点

console.log(Function);    
console.log(Object);

返回

function Function() { [native code] }
function Object() { [native code] }

可以看到这两个东西实际上都是函数,一个是Function()函数,一个是Object()函数

参考 如果函数B在对象A的原型链 (prototype chain) 中被发现,那么instanceof操作符将返回true,否则返回false.

所有函数(上图中的Foo(),Object()和Function())的__proto__ 都是Function.prototype

其实图很清楚,最关键的是你一定要记住instanceof左边的是对象,右边的是函数,这个关键字其实只是测试

        左边的对象的__proto__是否可以关联到右边创建它的函数的prototype

console.log(Function instanceof  Object); // true

console.log(Object instanceof Function);  // true

这两行代码中的第一行Function是对象(函数对象) , Object是类型

第二行中的Object是对象(还是函数对象) , Function是类型

通过上面的内容可以很好理解为什么通过

var fcuntion1 = function(){};  //调用function()函数获得

var fcuntion 2 = new Function();  //Function被new的时候还是调用function

都可以创建一个函数对象, 因为Function类型的constructor就是function(){}

如果你觉得上面的所有内容都不够”变态”, 实际上我们可以做得更”变态”一点

那就是__proto__属性其实是可以被重写的, 这意味着使用它时仍然不是那么太安全 ( 当然估计没人会给自己惹这种麻烦 )

function fn(){}
var obj = {}


console.log(obj instanceof Function); //false

obj.__proto__ = Function.prototype;

console.log(obj instanceof Function); // true

在这个”变态”的例子中我不得不提一下typeof

typeof用以获取一个变量或者表达式的类型,typeof一般只能返回如下几个结果:

number,boolean,string,function(函数),object(NULL,数组,对象),undefined

所以这玩意跟instanceof完全是两码事

在javascript中如果你需要调用一个函数, 需要在函数后使用调用运算符”()”.

node express 多线程

我们知道标准的Javascript是单线程的, 无法真正的使用多核cpu, 过去的做法是在不同的端口上启动多个相同或者不同的服务

例如如果一个服务要运行在多个核心上, 就必需使用脚本启动在多个端口上, 然后用负载均衡服务器对请求进行分发, 实现起来较为复杂

使用cluster集群api可以让单个程序充分利用多核cpu, 下面来看看步骤

首先需要安装下面两个模块

npm install –save cluster
npm install –save os

然后是实现代码

var cluster = require("cluster");

var http = require("http");

var numCPUs = require("os").cpus().length;

if (cluster.isMaster) {

  // Fork workers.

  for (var i = 0; i < numCPUs; i++) {

    cluster.fork();

  }

 

  cluster.on("exit", function(worker, code, signal) {

    console.log("worker " + worker.process.pid + " died");

  });

} else {

  // Workers can share aclearny TCP connection

  // In this case its a HTTP server

  http

    .createServer(function(req, res) {

      res.writeHead(200);

      res.end("hello world\n");

    })

    .listen(3000);

}

cluster会在线程之间共享一个端口, 当有外部请求到达时, cluster会将这个请求转发到随机的子线程中.

这里的运行机制有点类似Nginx, 是由一个线程池 ( 通常大小为cpu物理核心数 ) 来处理所有请求, 而不是像Tomcat那样为每个请求创建一个线程.

当程序被运行后, isMaster会被设置为true, 然后就进入了第一个if块中, 在其中调用了 cluster.fork() 之后, 程序会创建一个子线程, 并重新运行, 这时cluster.isMaster会被设置成false, 需要注意的是这里有一个循环, 用于一次性创建多个子线程. 

下面这个例子里将演示如何在express环境中使用cluster多线程

express使用bin文件夹下的www脚本启动http server, 所以直接修改这个脚本就可以使用cluster

app/bin/www 将此脚本的内容修改为

#!/usr/bin/env node

 

var app = require("../app");

var debug = require("debug")("app:server");

var http = require("http");

 

var cluster = require("cluster");

var numCPUs = require("os").cpus().length;

 

if (cluster.isMaster) {

  console.log("[master] " + "start master...");

 

  for (var i = 0; i < numCPUs; i++) {

    cluster.fork();

  }

 

  cluster.on("listening", function(worker, address) {

    console.log(

      "[master] " +

        "listening: worker" +

        worker.id +

        ",pid:" +

        worker.process.pid +

        ", Address:" +

        address.address +

        ":" +

        address.port

    );

  });

} else if (cluster.isWorker) {

  console.log("[worker] " + "start worker ..." + cluster.worker.id);

  var server = http.createServer(app);

  server.listen(3000);

  server.on("error", onError);

  server.on("listening", onListening);

}

 

var port = normalizePort(process.env.PORT || "3000");

app.set("port", port);

 

function normalizePort(val) {

  var port = parseInt(val, 10);

 

  if (isNaN(port)) {

    // named pipe

    return val;

  }

 

  if (port >= 0) {

    // port number

    return port;

  }

 

  return false;

}

 

function onError(error) {

  if (error.syscall !== "listen") {

    throw error;

  }

 

  var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;

 

  // handle specific listen errors with friendly messages

  switch (error.code) {

    case "EACCES":

      console.error(bind + " requires elevated privileges");

      process.exit(1);

      break;

    case "EADDRINUSE":

      console.error(bind + " is already in use");

      process.exit(1);

      break;

    default:

      throw error;

  }

}

 

function onListening() {

  var addr = server.address();

  var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;

  debug("Listening on " + bind);

}

再说闭包

已经记不清是哪个本书的哪个作者说过, 如果你不能把一个问题给6岁的孩子讲明白,那么你自己其实也还是没有明白

闭包这个概念其实已经被提及很多年了,java8是否支持闭包当初在csdn上也争论了许久(结果它弄了一个Lambda)

即便是现在出去找工作也经常会被问到, 什么是闭包 ( 相信我,我做面试官的时候从来没问过这个问题 )

今天觉得有必要重新整理一下关于闭包的知识

1 作用域

2 作用域

3 作用域

重要的事情说三遍

这里我们还是先以javascript为例

对变量作用域的控制 是javascript语言 实现闭包的必要手段

所谓的闭包, 在javascript中就是在函数B中(作用域B), 能够访问到函数A(作用域A)的变量

强调一下javascript只有全局变量(不带var),和局部变量(带var)

假设我们有函数A和函数B

function A(){

var test = 1;

}

function B(){

}

那么如何能使用函数B访问到函数A中的局部变量test呢 ( 当然了,如果你使用全局变量或者参数传递我们就不用在这啰嗦闭包了)

在javascript中, 只有一个函数内部的子函数,能访问这个函数的局部变量

接下来我们把这个函数修改一下,让函数B成为函数A的一部分

function A(){

    var test = 1;

    function B(){

        test = test+1;

        alert(test);

    }

}

现在我们可以说, 这段代码是闭包了么, 是,但是也不完全是, 现在的问题是, 我们只是有一个函数和它的子函数,如何直接通过子函数(function B)来访问到主函数的局部变量(test)是眼前的问题,手段其实挺多的

1 你可以声明一个全局变量,然后指向函数B,这样你就可以在函数A的外部通过这个变量调用函数B

2 你还可以让函数A返回B,然后赋给一个全局变量

3 最后你还可以选择把函数B当参数传出来

第一种方式

<html>

<head>

<script type="text/javascript">

var global;

function A(){
    
    var test = 1;
    
    function B(){
        test = test+1;
        alert(test);
    }
    
    global = B;
}

function run(){
    global();
}

A();

</script>

</head>

<body>

<input type="button" value="run" onclick="run()"/>

<div id="plane">

</div>

</body>

</html>

第二种方式(网上这种例子比较多哈)

<html>

<head>

<script type="text/javascript">

function A(){
    
    var test = 1;
    
    function B(){
        test = test+1;
        alert(test);
    }
    
    return B;
}

function run(){
    global();
}

var global = A();

</script>

</head>

<body>

<input type="button" value="run" onclick="run()"/>

<div id="plane">

</div>

</body>

</html>

第三种方式(感觉是写起来最麻烦而且完全没必要,这里就是为了折腾一下)

<html>

<head>

<script type="text/javascript">

var global;

function A(){
    
    if(test==undefined){
        var test = 1;
    }
    
    
    function B(){
        test = test+1;
        alert(test);
    }
    
    transfer(B);
}

function transfer(fn){
    global=fn;
}

function run(){
    global();
}

A();

</script>

</head>

<body>

<input type="button" value="run" onclick="run()"/>

<div id="plane">

</div>

</body>

</html>
 

Nodejs 异步I/O

单线程同步编程模型会因阻塞I/O导致硬件资源得不到更优的使用

多线程编程模型也因为编程中的死锁, 状态同步等问题让开发人员头疼.

Node在两者之间给出了它的方案, 利用单线程, 远离多线程死锁, 状态同步等问题, 利用异步I/O, 让单线程远离阻塞, 以更好的使用CPU

异步I/O可以算做Node的特色 , 因为它是首个大规模将异步I/O应用在应用层上的平台, 它力求在单线程上将资源分配得更高效

为了弥补单线程无法利用多核CPU的缺点, Node提供了类似前端浏览器中Web Workers的子进程, 该子进程可以通过工作进程高效地利用CPU和I/O

Node 自身的执行模型为 事件循环, 正是它使得回调函数十分普遍

在进程启动时, Node便会创建一个类似于while(true)的循环, 每执行一次循环体的过程我们称为Tick. 每个Tick的过程就是查看是否有事件需要处理, 如果有, 就取出事件及其及相关的回调函数. 如果存在相关的回调函数, 就执行它们, 然后进入下一次循环, 如果不再有事件处理, 就退出进程.

观察者

在每个Tick的过程中如何判断是否有事件需要处理呢, 这里必须要引入的概念是观察者

每个事件循环中有一个或者多个观察者, 而判断是否有事件要处理的过程就是向这些观察者询问是否有要处理的事件

事件循环是一种典型的 生产者/消费者 模型,   异步I/O,网络请求等是事件的生产者, 这些事件被传递给观察者, 事件循环则从观察者哪里取出事件并处理.

在windows下, 这个循环基于IOCP创建, 在Unix和Linux下则基于多线程创建

从Javascript发起调用到内核执行完I/O操作的过程中, 存在一种中间产物, 它叫做请求对象

事件循环, 观察者, 请求对象, I/O线程池这四者共同构成了Node异步I/O模型的基本要素

同步式

对于同步式的服务,一次只能处理一个请求, 并且多余请求都处于等待状态

每进程/每请求

为每个请求启动一个进程, 这样可以处理多个请求, 但是它不具备扩展性, 因为系统资源只有那么多

每线程/每请求

为每个请求启动一个线程来处理. 尽管线程比进程要轻量, 但是由于每个线程都占用一定内存, 当大并发请求到来时, 内存将会很快用光, 导致服务器缓慢.

每线程/每请求的方式目前还被Apache所采用. Node通过事件驱动的方式处理请求,无须为每个请求创建额外的对应线程, 可以省掉创建线程和销毁线程的开销, 同时操作系统在调度任务时,因为线程较少, 上下文切换的代价很低. 这使得服务器能够有条不紊地处理请求, 即使在大量连接的情况下,也不受线程上下文切换开销的影响

ECMAScript6 (1)

ES6 中使用let和const 都可以声明变量, 但是它们与var不同

虽然var可以声明局部变量但是无论在哪里声明都会被当成在当前作用域顶部声明的变量, 这就是提升机制

function getValue(condition){

    if(condition){

        var value = "blue";

        return value;

    }else{

        return null;

    }

}

上面的代码编译后等价于

function getValue(condition){

    var value;

    if(condition){

        value = "blue";

        return value;

    }else{

        return null;

    }

}

这个机制通常会导致当condition为true时, 在else块中也可以访问到value变量, 只不过因为它只是通过var value; 声明过并没有进行初始化

所以会得到一个undefined

将上述代码的var替换成let之后,  当condition为true时, else块中是无法访问到value变量的, 离开作用域块之后value会被销毁

同样, let声明的变量不可以重名

const用于声明常量, 一旦声明无法更改, 在声明时必须立即进行初始化

这里实际上和java的final一样, 虽然const声明常量, 但是如果值是一个引用, 你还是可以修改这个引用指向的对象的属性值

Javascript引擎在扫描代码发现变量声明时, 如果遇到var, 就把声明提升到作用域顶部, 如果遇到let或者const, 则会把声明放入临时死区(TDZ, temporal dead zone)

ECMAScript6 (2) 函数

Javascript 中, 无论函数定义中声明了多少形参, 都可以传入任意数量的参数,

也可以在定义函数时添加针对参数数量的处理逻辑, 当已定义的形参无对应的传入参数时为其指定一个默认值

在ECMAScript5 和早期版本中, 你可以通过以下方式创建函数并赋上默认值

function testArguments(a,b,c){
    b = "default b ";
    c = "default c ";
    
    console.log(a + b + c);
}

testArguments("a ");

上面的代码会输出

a default b default c 

在ECMAScript6中, 上面的代码等价于

function testArguments(a,b="default b ",c="default c"){
    console.log(a + b + c);
}

testArguments("a ");

这里重点来看一下箭头函数 

在ECMAScript6中, 箭头函数是一种使用 => 定义函数的新语法, 但是这种方式创建的函数与传统Javascript函数有些许不同

1. 没有this, super, arguments 和 new.target 绑定

箭头函数中的 this, super, arguments 和 new.target 这些值由外围最近一层非箭头函数决定

2. 不能通过new关键字调用

箭头函数没有 construct 方法, 所以不能通过new关键字调用, 程序会抛出错误

3. 没有原型

由于不可以通过new关键字调用箭头函数, 因为没有构建原型的需求, 所以箭头函数不存在prototype这个属性

4. 不可以改变this绑定

函数内部的this值不可以被改变, 在函数的生命周期内始终保持不变

5. 不支持arguments对象

6. 不支持重复命名参数

let sum = (x,y) => x+y;

等价于

let sum = function(x,y){
    return x+y;
}

ECMAScript6 (3) 对象

ECMAScript(5) 中如果为对象添加方法, 必需通过指定名称并完整定义函数来实现, 例如下面的例子

var person = {
    name:"Li",
    sayName: function(){
        console.log(this.name);
    }    
}

person.sayName();

在ECMAScript6中等价于

var person = {
    name:"Li",
    sayName(){
        console.log(this.name);
    }    
}

person.sayName();

对象字面量的语法形式是在一个赋值操作符左边放置一个对象字面量

let student = {
        name:"Li",
        age:"20"
}

let {name,age} = student;

console.log(name);
console.log(age);

ECMAScript6 (4) 类

类的声明

ECMAScript6中有一种与其他语言中类似的类特性: 类声明

同时它也是ECMAScript6中最简单的类形式

要声明一个类

class Student{
    constructor(name){
        this.name = name;
    }
    sayName(){
        console.log(this.name);
    }
}

new Student('Li').sayName();

然后强调一下构造器中的this

其值会被绑定到调用this的函数的最近的父对象

var person = {
  first: 'John',
  last: 'Smith',
  full: function() {
    console.log(this.first + ' ' + this.last);
  },
  personTwo: {
    first: 'Allison',
    last: 'Jones',
    full: function() {
      console.log(this.first + ' ' + this.last);
    }
  }
};

person.full();
//输出 'John Smith'
person.personTwo.full();
//输出 'Allison Jones'

这里React构造函数中经常写的

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
}

bind()方法会创建一个新函数,当这个新函数被调用时,它的this值是传递给bind()的第一个参数, 它的参数是bind()的其他参数和其原本的参数. 

也就是说, this.handleClick.bind(this); 这句代码执行之后会创建一个新函数, 然后把这个函数赋值给等号左边的原函数引用( 当然你也可以起名叫 this.newHandleClick )

rendor函数中的 

<button onClick={this.handleClick}> {this.state.isToggleOn ? ‘ON’ : ‘OFF’} </button>

调用的实际上是新函数, 调用这个新函数的this值也就赋给了bind的参数this, 也就传入了handleClick函数的内部

如果去掉这句绑定, 那么构造函数中的this和handleClick函数中的this就成了不同的this

React browserHistory.push 传参

传参方式1:

将参数直接写在路径中,前提是router定义时,需要指定参数的名字,这里假定为:value;

browserHistory.push(pathname: '/routerName/123');

如何获取到123呢?

通过this.props.params.value即可获取

传参方式2:

通过state传参,可以在页面跳转时,url中不显示参数;

browserHistory.push(pathname: '/routerName',state: { key: value });

跳转界面如何接收参数呢?

this.props.location.state.key便可以获得state中的key的value;

尝试了第二种方式发现不好用, 可能是router版本更新到4之后网上的内容太旧了

经过推敲之后发现可以这样

var data = {issueId:60};  
var path = {  
     pathname:’/IssueDetail.html’,  
     state:data,  
}  
this.props.history.push(path);

然后接收的时候要带着withRouter

export default withRouter(IssueDetailDetails);

id:_this.props.location.state.issueId

React ES6 优化笔记

以下内容为最近一段时间使用React的优化笔记

1. 使用解构传递参数

const AddPlayerForm = ({ values, onSubmit, onChange }) => {

    return (…)

}

2. promise函数中不一定要使用_this


          .then(response => {
              if(this.isMountedDone) {
                  this.setState({
                      options: response.data
                  });
              }

3.     Stop Memory Leaks with componentWillUnmount Lifecycle Method in React

    componentDidMount() {
        this.isMountedDone = true;
        if(this.props.loadByDb===”true”){
            this.loadData();
        }

    };
    
    componentWillUnmount(){
        this.isMountedDone = false;
    }

loadData(){
        let url = Global.serverpath+’/api/v1/postlogin/sysparameters’;
         axios.get(url, {
            params: {
              module_key:this.props.module_key,
              value_key:this.props.value_key
            },
            headers: {
              “lira_token”: Global.getCookie(‘lira_token’)
            }
          })
          .then(response => {
              if(this.isMountedDone) {
                  this.setState({
                      options: response.data
                  });
              }
          }).catch(function (error) {
            alert(“load error”+JSON.stringify(error));
          });
    }

4. JSX 中使用if else

{errors.name && <AlertBox>{errors.name}</AlertBox>}

上面语句的语义在于, 先执行errors.name 如果返回为true, 则继续执行<AlertBox>, 而AlertBox实际是一个调用

反之, 使用 || 

{errors.name || <AlertBox>{errors.name}</AlertBox>}

如果errors.name已经返回了true, 则 or 后面的内容会被忽略

5. Component引用与调用

下面的例子是一个对于PlayerForm的引用

<Formik>

{PlayerForm}

</Formik>

下面是调用

<Formik>

    <div>{PlayerForm}</div>

</Formik>

区别在于一个传递的是引用,类似<Test callback={myfunction} />

另一个传递的是调用后的返回值(当然如果是一个方法也没有问题)<Test callback={myfunction()} />

6. 拼接字符串

this.setState({ players: […this.state.players, player] });