分类存档: javascript

PWA 学习小结

  1. 创建主屏幕入口

<link rel="manifest" href="manifest.json" />

manifest.json文件大概是这样的

{
  "name": "微博Lite",
  "short_name": "微s博",
  "description": "随时随地分享新鲜事",
  "icons": [{
      "src": "https://luoyongjie.cn/lab/pwa/icon/weibologo.png",
      "sizes": "606x606",
      "type": "image/png"
    }],
  "start_url": "/static/pwa/",
  "scope": "/static/pwa/",
  "display": "browser",
  "orientation": "portrait",
  "background_color": "#F3F3F3",
  "theme_color": "#F3F3F3",
  "related_applications": [],
  "prefer_related_applications": false
}

 

这里我是抄m.weibo.com/beta的manifest.json

内容太多,不一一称述,无非就是设置logo图片,访问地址,app打开的屏幕,是否显示导航栏,就像一个HirdApp一样

这里说一下关键问题

  1. 设置主入口的交互是如何的?

image.png

image.png

点击添加之后就会在桌面出现icon入口了

感觉这样的交互,用户几乎不会这样做,太鸡肋了,那么有没有可以用js控制提醒用户是否需要添加到主屏幕的方式呢?没有。。。。。

于是我带着怀疑的态度,尝试了

chrome IOS版本 居然没有开放这个操作

chrome Android版 操作有了,确认添加到主屏幕了,但是屏幕就是找不到icon入口

2.server worker 离线缓存

入口文件处载入如下代码:

<script>
function registerServiceWorker(){
    return navigator.serviceWorker.register('./sw.js').then(registration => {
        alert('注册成功');
        return registration;
    })
    .catch(err => {
        alert('注册失败', err);
    });
}
window.onload = function () {
    if (!('serviceWorker' in navigator)) {
        return;
    }
    registerServiceWorker()
}
</script>
const CURCACHE = 'CURCACHE_test_1'
const RUNTIME = 'runtime';
var CURCACHE_URLS = [
  '/lab/pwa/index.html',
  '/lab/pwa/main.css'
];

self.addEventListener('install',e=>{
    e.waitUntil(
      //存储缓存路径对应的资源
        caches.open(CURCACHE).then(cache=>{
            cache.addAll(CURCACHE_URLS)
        }).then(
            self.skipWaiting()
        )
    )
})
 
 
   
  //代理请求,使用缓存,请求发送之前
  self.addEventListener('fetch', e => {
    e.respondWith(
      //缓存是否匹配 
      caches.match(e.request).then(function(response) {
        if (response != null) {
          //命中缓存返回缓存,结束请求
          return response
        }
        //未命中缓存,正常请求
        return fetch(e.request.url)
      })
    )
  });

 image.png

只要caches设置了缓存,就能在离线状态下访问页面,同时可以在sw中控制缓存哪些内容。

sw.js文件每次都会请求服务器获取最新的server worker文件

注意:如果浏览器本身对sw.js进行缓存的话,也不会得到最新代码,所有代码会变成死代码,无法更新。所以对sw文件最好配置成cache-control: no-cache

经过测试,ios safair,chrome 都支持,但是微信页面不支持 navigator.serviceWorker == undefined,估计是微信为了推广小程序,故意封掉的

3.消息推送

https://firebase.google.com/docs/web/setup

按照这里的教程可以利用firebase的安装包,可以做出一个消息推送的demo。不过必需要翻墙。

实际体验如下:

需要在服务器环境模拟一个请求到google的推送服务器

比如

curl -X POST -H "Authorization: key=YOUR-SERVER-KEY" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "Portugal vs. Denmark",
    "body": "5 to 1",
    "icon": "firebase-logo.png",
    "click_action": "http://localhost:8081"
  },
  "to": "YOUR-IID-TOKEN"
}' "https://fcm.googleapis.com/fcm/send"

然后在安装好firebaseSDK的页面上打开

电脑端:焦点在当前窗口的话,会即使调用当前页面的js messaging.onMessage 方法收到msg

            如果焦点不在当前窗口,则启动ServerWorker后台的self.registration.showNotification

        image.png

        浏览器就会在系统层发出一个消息通知。

        如果断网的情况下,等到网络恢复,这个消息会自动弹出来

手机端:ios的 微信,safair,chrome 完全不兼容

            android的chrome 比较乐观

            1541054402791967.png

           可是只是收到几次,突然就不行了,不知道是不是系统做了限制,卸载再安装又可以收到几次。

            如果把chrome进程杀掉,也是收不到推送的,这一点挺坑的。

          如果要关注兼容问题,可随时关注 

            https://caniuse.com/#search=Push%20API

            https://caniuse.com/#search=Notifications 这个API的兼容情况

小结:

注意以上所有环境都需要在https或localhost或127.0.0.1进行,因为server worker规定了只能在这些调节下才能开启。如果是用手机做测试,那你必需拥有一个https的站点才能测试,这一点非常坑,于是我把站点升级成https了。

实际情况:PWA最重要的一点是离线缓存,(首屏入口和消息推送用的场景不是刚需)提高用户首屏加载体验,可是这么关键的一点,居然被微信封杀了,在中国h5的流量几乎都在微信,场景限制非常大。如果某个h5不是在h5推广的,我觉得pwa离线缓存是提高用户体验的利器。

vue响应式源码实现

png

const Observer = function(data) {
  // 循环修改为每个属性添加get set
  for (let key in data) {
    reactive(data, key);
  }
}

const Dep = function() {
  const self = this;

  //这里可以用数组,就能实现同一个data,观察多个vue实例
  this.depend = ()=>{
    if (Dep.target) {
	  this.sub = Dep.target
	}
  }

  this.notify = ()=>{
	  this.sub.update();
  }
}

const Watcher = function(vm, fn) {
  //这样的写法是为了把target传到dep实例里面,当然也可以不挂载到dep,但要有一个地方暂存起来
  Dep.target = this;

  this.update = function() {
    console.log('in watcher update');
    fn();
  }

  //初始化运行一次render函数
  fn();

  Dep.target = null;
}

const reactive = function(obj, key) {
	const dep = new Dep();
	let val = obj[key];
	Object.defineProperty(obj, key, {
		enumerable: true,
		configurable: true,
		get() {
		  console.log('in get');
		  dep.depend();
		  return val;
		},
		set(newVal) {
		  if (newVal === val) {
			return;
		  }
		  val = newVal;
		  dep.notify();
		}
	});
}

const Vue = function (options) {
	this._data = options.data.call(this);

	//设置data的get,set
	Observer(this._data);

	this.mount = (id)=>{
		this.dom = document.querySelector(id);
		new Watcher(this, this.render);
	}

	//渲染函数,这里主要是触发get方法
	this.render = ()=>{
		//这里vue应该会有虚拟dom的算法实现
		this.dom.innerHTML = `<div>${this._data.text}</div>`;
	}
}


const vue = new Vue({
  data() {
    return {
      text: 'hello world'
    };
  }
})
vue.mount('#app');
vue._data.text = '123';

首先看一下主入口 Vue,包含 Observer, mount, render 三个方法

其中 Observer 的作用是为了获取data中的每一个get,set操作,以达到监听数据变化的手段(可用ES6的Proxy实现)

Watcher函数有2个作用,第1个是在程序首次运行的时候,用于触发Observer中的get方法,收集依赖,并把当前watcher的实例绑入到dep中。

                                    第2个作用是,当数据被修改,也就是触发set操作的时候,通知对应的dep中的watch实例触发updata->render,达到页面重新渲染的目的。

render函数这里只是简单的一行代码,实际vue里面的实现应该是非常复杂的,包括 jsx,vue模版引擎,虚拟dom之类的实现

github地址: https://github.com/gaxking/vue-deeplearn 

​《你不知道的Javascript-中册》阅读笔记

第一部分

第一章

1、

var a;

a; // undefined

b; // ReferenceError: b is not defined

undefined 和 is not defined 是两回事

2.

// 这样会抛出错误

if(DEBUG) {

    console.log( "Debugger is starting" )

}

//这样是安全的

if (typeof DEBUG !== "undefined") {

    console.log( "Debugging is starting" )

}

第二章

1.

var b = 42.;  //42.这样的写法没问题,只是不常见,但从代码的可读性考虑,不建议这样写。

42.toFixed(3) 是无效语法,因为 . 被视为常量42.的一部分 , 42..toFixed(3) 则没问题

2.

Object.is() 主要是用来处理那些特殊的相等比较 

Object.is(NaN, NaN) //true

Object.is(0, -0) //true

3.

对包含循环引用的对象执行JSON.stringify(..) 会出错

第4章

1.

以下这些是假值:

undefined

null

false

+0, -0 和NaN

""

假值的布尔强制类型转换结果为false

理论上 假值列表以外的都应该是真值。  

2.

使用 Date.now() 来获取当前时间戳,比new Date().getTime() 简单

3.

 ~x 大致等于 -(x+1) 

~42; // -(42+1) ==> -43

4.

var a = "Hello World";

if(a.indexOf("ol") == -1){

    //没有匹配

}

这里是指用-1作为失败时的返回值,这些细节应该被屏蔽。

var a= "Hello World";

~a.indexOf("lo"); // -4 真值

if (~a.indexOf("of")) {  //true

    //找到匹配

}

~a.indexOf("ol"); //  0 <–假值

!~a.indexOf("ol"); // true

if(!~a.indexOf("ol")){ //true

    //没有匹配!

}  

5.

~~49.6

~~x 能对小数进行截取,取代Math.floor(xxx),但需要注意负数的情况

~~-49.6 // -49,    ~~中的第一个~执行ToInt32并反转字位,然后第二个~再进行一次字位反转,即将所有字位反转回原值,最后得到仍然是ToInt32的结果

Math.floor( -49.6 ) //-50

6.

[] + {} //[Object object]   "" + [Object object] 

{} + [] //0     {}被当作一个独立的空代码快,不执行任何操作。  代码块结尾不需要分号, 最后+[] 将[]显示强制类型转换

7.

true || false && false;  //true
(true || false) && false;  //false 
true || (false && false);  //true

这说明&&运算符优先于||执行

8.

function foo( a = 42, b = a+1) {
    console.log(
        arguments.length, a, b,
        arguments[0], arguments[1]
    )
}
foo();                             //0 42 43 undefined undefined
foo( 10 );                       //1 10 11 10 undefined
foo( 10, undefined );    //2 10 11 10 undefined
foo( 10, null );              //2 10 null 10 null

在ES6中,虽然参数a和b都有默认值,但是函数不带参数时,arguments数组为空.

如果向函数传递undefined值,则arguments数组中会出现一个值为undefined的单元

9.

<script>foo();</script>

<script>

    function foo() { .. }

</script>

报错,不会进行变量提升

10.

在调试的过程中,有可能对象在console.log(…)语句之后被修改,看到了意料之外的结果,要意识到 这可能是I/O的异步造成的,可以用JSON.stringify()快照一次

11.

《你不知道的Javascript-上册》阅读笔记

第一部分  作用域和闭包

第三章

 1.

{
    console.log( bar ); //Error
    let bar = 2;
}

说明1. let 函数没有变量提升这个说法, 

       2. 在大括号内就能产生块作用域

第四章

1.

foo(); //error
{
function foo() {console.log("b")}
}

说明函数的提升只在自己的块级作用域有效

console.log(a) //undefined 注意不是error
{
var a;
}

说明var的声明提升可以在子级的块级作用域有效

2.

JavaScript中的作用域就是词法作用域

function foo() {
  console.log(a); //2
}
function bar() {
  var a = 3;
  foo();
}
var a = 2;
bar();

3.

var fun = function foo() {console.log("b")}
fun();  //b
foo(); // error

说明同时使用2个变量命名的函数,会忽略后面一个

第二部分 this解析

当判断this的真实对象时,需要应用下面四条规则中的其中一条:

  1. 默认绑定

     this 一般指向全局对象,严格模式是undefined

      function foo(){
            console.log(this.a);  //use strict 只对当前块级作用域有效,所以这里不是undefined
     }
    var a = 2;
    
    (function(){
        "use strict";
        foo();//2
    })();

    foo() 是直接使用不带任何修饰的函数进行调用的,因此属于默认绑定

    2.隐式绑定

    obj.foo()  //类似这种有上下文调用的

    3.显示调用

     call,apply,bind

    4. new 一个实例

2. 

function foo() {
    console.log(this.a);
}
var a = 2;
foo.call( null );//2

如果call,apply,bind传入null,则应用默认绑定

3.

function foo()
{
    return (a)=> {
    console.log(this.a)
    }
}
 
var obj1 = {
    a:2
}
 
var obj2 = {
    a:3
}
 
var bar = foo.call(obj1);
bar.call(obj2); //2

foo()调用一次时,箭头函数中的this获得 obj1 , 由于箭头函数一旦获得this,就无法被修改,所以还是obj1

 第三章 对象

  1. 深度复制对象时,要注意循环引用的问题

第五章 原型

  1. 判断两个对象之间的关系,可以用

 b.isPrototypeOf( c );

2. 使用Class的一个缺点:

class C{
    constructor{}{
        C.prototype.count++
        console.log("Hello:" + this.count);
    }
    C.prototype.count = 0;
    var c1 = new C();
    //Hello:1
    var c2 = new C();
    //Hello:2
    c1.count === 2;//trte
    c1.count === c2.count;//true
}

这种方法问题是,它违背了class语法的本意。在实现中暴露了.prototype

如果使用 this.count++ 我们会发现 c1,c2上都创建了 .count属性,而且互相共享

《ES6 标准入门》 阅读笔记

第三章

    1.var {foo:baz} = {foo:"aaa", bar:"bbb"}   //bas = bbb, foo = undefined

    对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者

    

    2.嵌套赋值的例子,平时开发经常会用到

    let obj = {};

    let arr = [];

    ({foo:obj.prop, bar:arr[0]}) = {foo:123, bar:true};

    obj //{prop:123}

    arr //[true]

   3.只要等号右边的值不是对象,就先将其转为对象。

    let {toString:s} = 123;

    s === Number.prototype.toString //true

    

    let {toString:s} = true

    s === Boolean.prototype.toString //true

    4. 交换变量

    es6 :[x, y] = [y, x];

    想到一种es5的写法 x = [y, y = x][0]

第五章

    1.正则表达式的  y修饰符,y修饰符隐含了头部匹配的标志

    var s = "aaa_aa_a";

    var r1 = /a+/g;

    var r2 = /a+/y;

    r1.exec(s) //["aaa"]

    r2.exec(s) //["aaa"]

    r1.exec(s) //["aa"]

    r2.exec(s) //null

    var s = "aaa_aa_a";

    var r = /a+_/y   //换一下表达式

    r.exec(s) //["aaa_"]

    r.exec(s) //["aa_"]

第七章

    1.Array.from() 作用是把类数组的对象转化为真正的数组, 适用于(arguments对象,NodeList,Iterable对象,Set,Map)

    let arr = {'0':'a', '1':'b', '2':c, length:3};

    ES5:

    var arr1 = [].slice.call(arr);

    ES6

    let arr2 = Array.from(arr);

    2.Array.includes()

    [1,2,3].includes(2) //true 方法属于es7

    3.数组推导,可以在[]内用循环创建数组

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

    [for (year of years) if(year>3)]   //[4,5,6,7] 

第八章

    1.

    function m1({x = 0, y = 0}){

        return [x, y];

    }

    function m2({x, y} = {x:0, y:0}){

        return [x,y];

    }

继续阅读 »

babel插件编写

    之前项目上遇到一个问题,服务器请求api回来的对象如果位空的时候是{list:null, name:null}, 我们期望的值是{list:[], name:""} ,但是后台由于某些原因并没有帮我们做到过滤。于是我们在敲业务代码的时候,经常由于数据问题,导致整个页面挂掉。比如

    let obj ={list:null, name:null}; //这是服务器返回的,不可控
        obj.list.map(()=>{}) //list为null 报错
        obj.name.length //name为null 报错

        虽然说可以在使用之前调用以下判断代码,确保安全

        obj.list = obj.list===null?[] || obj.list
        obj.name = obj.name===null? "" || obj.name;

    但是人为代码难免遗漏,数据难预测,多层嵌套等原因,很难杜绝这种问题。于是有一天突然想到能不能用babel插件彻底解决这个问题呢?

    比如在运行

    function hello(){
        obj.list = obj.list===null?[] || obj.list;   //编译的时候,babel插件帮我加上这句话
        return obj.list.map(v=>{
        }) ;
    }

    这样就几乎能杜绝这个问题。

    于是边开始了这个课题:babel插件编写

    开始之前先看看资料

    

    从零开始写babel插件

    http://eux.baidu.com/blog/2017/12/how-to-write-babel-plugin?utm_source=tuicool&utm_medium=referral

    Babel插件开发指南

    https://github.com/brigand/babel-plugin-handbook/tree/master/translations/zh-Hans

    AST 辅助工具

    http://astexplorer.net/

    我开发的例子

    https://github.com/gaxking/babel-plugin-gax-killnull

    上手要点:

    1. 一定要在 node_modules 编写插件

    2. plugin 和 preset 的排序问题

  • Plugin 会运行在 Preset 之前。

  • Plugin 会从第一个开始顺序执行。

  • Preset 的顺序则刚好相反(从最后一个逆序执行)。

    3. 多尝试http://astexplorer.net/,了解AST语法构造,因为截取代码和插入代码都需要它的知识

    

关于JS小数运算不精确的探讨

各位老司机应该知道,很多时候做小数运算的时候会出现小数溢出。常见的场景是做价格运算,测试经常报bug。

于是针对这个奇特的问题,开始了寻根之旅。

举个例子:0.2+0.1=0.30000000000000004

为什么会这样?

其实很多其他计算机语言都有这个问题。计算机中的所有运算都会经过二进制转换再计算的。

0.2的二进制是  

0.2 * 2 = 0.4 取整数位  0  得 0.0

0.4 * 2 = 0.8 取整数位  0  得 0.00

0.8 * 2 = 1.6 取整数位  1  得 0.001

0.6 * 2 = 1.2 取整数位  1  得 0.0011

0.2 * 2 = 0.4 取整数位  0  得 0.00110

(n)….

太长了 于是直接在控制台上打印 

var num = 0.2;

num.toString(2)

// "0.001100110011001100110011001100110011001100110011001101"

同理算出 0.1的二进制为

// "0.0001100110011001100110011001100110011001100110011001101"

两个二进制相加得出    

// "0.0100110011001100110011001100110011001100110011001100111"

根据这个IEEE754的标准 就会把最后的 1 舍去变成

0.0100110011001100110011001100110011001100110011001101

然后转成十进制得出结果是 0.30000000000000004

再举个例子 0.1+0.3 = 0.4 

为什么这两个数不会产生溢出呢?

算出 0.1的二进制为

// "0.0001100110011001100110011001100110011001100110011001101"

算出 0.4的二进制为

// "0.01100110011001100110011001100110011001100110011001101"

两个二进制相加得出    

// "0.1000000000000000000000000000000000000000000000000000001"

根据这个IEEE754的标准 就会把最后的第52位进一舍零,得到

"0.1000000000000000000000000000000000000000000000000000"

剩下0.1 转换十进制为0.5

找了网上的一张图,其实还不是完全看懂指数位是什么。。。。

QQ图片20171226234411.png

继续阅读 »

狂啃犀牛书-笔记

第二章

x

++

y

解析为 x;++y;而不是x++;y;

第三章

  1. 只有 null 和 undefined 是无法拥有方法的值。

  2. js能表示的数字范围是(-2^53~2^53), 因为采用IEEE754标准。对于这个标准的来龙去脉。不作深究。

  3. var s = "hello world"; s.charAt(0);//"h"  ,可用charAt(x)获取对应位置上的字符,中文也适用。

    也可用用s[s.length-1];//d

  4. 原始值(undefined,null,布尔值,数字,字符串):任何方法都无法改变原始值;

    var s = "hello world";  

    s[0] = "1"// s不变;

    s.toUpperCase(); //s不变,只是返回新的字符串。

  5. Boolean(-1)//true,只要是非0,就等于true

    Boolean([])//true

    Boolean({})//true

  6. !xx  //等价于 Boolean(x)

  7. 对象到字符串的转换:

    调用toString()->valueOf()->toString()->errror

    对象到数字的转换:

    调用valueOf()->toString()->number()->error

  8. 任何对象在左==或者"<",">"等作比较的时候,都会首先尝试调用valueOf(),判断是否能作比较,如果不能比较然后再调用toString();

    var obj = {};

    obj.toString = function(){return 0};

    false == obj //true

  9. JS 中var变量的块级作用域只存在于函数中,与{}无关。

    function test(){

      var i = 1;

      if(false){

          var j = 2;

      }

     console.log(j);//j==underfine 有定义,没赋值。

    10.当使用var 声名一个变量,创建的这个属性是不可配置的,也就是说这个变量无法被delete;

    11.在一层层作用域链中查找变量的过程叫做“变量解析”

第四章

     1. var x = [1,2,,,] //x = [1,2,undefined] ,无论后面有多少个逗号,都只有一个undefined

     2. 如果一个对象的创建不需要传入任何参数给构造函数的话,那么这对空圆括号是可以省略的; // new Object , new Data

     3. 数字除以0,等于正负无穷大,0除以0等于NaN     // 1/0 = Infinity,    0/0 = NaN

     4. 加号优先考虑字符串链接  // "1" + 1 = 11

         如果其中一个是对象则调用toString()之后相加,  // 1+ {} = 1[Object Object]

         其余情况会将两边转换为数组相加  //true+true = 2,  null + true = 1

     5. "++"运算符从不进行字符串连接操作  // var a = "a"; a++  => NaN

     6. 如果是非对象的 == 比较,如 true == 1 , null == undefined //会将非数字的转化为数字再比较

         如果其中一个是对象,则会调用toString(); //var s = {}; s == "[object Object]"

     7. 加号运算符更偏爱字符串,比较运算符则更偏爱数字 // "1" + 2 = 12,  "11" <  3 = false

     8. 如果instanceof 右操作数不是函数,则报异常

继续阅读 »

《JavaScript高级程序设计》第二次阅读笔记

第二章

  1. <!CDATA[   javascriptCode  ]]>   在XHTML中javascript可以用这个标记保证内嵌的javascript代码能正常运行,因为XHTML标准比较严格,很多标签都不能被解析。

  2. defer和async标记都不能保证是在DOMContentLoading之前或之后执行,区别就只是保证顺序执行而已。

第三章

  1. 严格模式下,使用未定义的变量会导致错误。

  2. nul值表示一个空对象的指针,而这也是使用typeof操作符检测null值时返回“object”的原因

  3. 无论在什么情况下都没必要把一个变量的值显式地设置为undefined,但是对于null,只要在保存[对象]的变了还没有真正保存对象,就应该明确地然它保存为null值。

  4. undefined 和 null,没有任何属性和方法

   5.要把都一个值转换为字符串,可以用加号把它与一个字符串“xxx”加在一起

   6.并非所有浏览器的BOM与DOM都属于Object

   7.+操作符 会像Number()一样对这个值执行转换。-号同理,不过变成负数。

   8.do-while 循环的代码至少会执行一次。

   9.for(; ;){ doSomething(); } 无限循环

   10.通过for-in循环输出的属性名的顺序是不可预测的,具体先后顺序因浏览器而异。

   11.swicth{case xxx:} 在比较时是使用全等操作符 ===,所以10不等于“10”

   12. 标准模式下arguments与参数会相互同步,严格模式下不会同步。

标准模式下:
function add(a){
arguments[0] = 10;
console.log(a); //10
}
add(1);
严格模式下
function add(a){
        "use strict";
arguments[0] = 10;
console.log(a);//1
}
add(1);

第4章

  1. 给基本类型添加引用会被浏览器直接忽略,也不会报错。

   2.要检测一个变量是不是基本数据类型用typeof,typeof null 等于object。如果用instanceof操作符检测基本类型的值,则翻回false.

第5章

   1.对象的key只能用字符串作key

   2.数组的toString()方法会返回由数组中每一个值的字符串形式拼接逗号分隔而成的字符串。所以如果[{a:1},2] toString() 等于 [object Object],2

   3.concat()方法可以添加字符串,也可以添加数组。如:

    var colors = ["red","green","blue"];

    var colors2 = colors.concat("yellow",["black","brown"]);

    alert(colors);//red,green,blue

    alert(color2);//red,green,blue,yellow,black,brown

   4.Array的5个迭代方法,都是给定运行参数function(value,index,array){}

    1.every() //返回布尔值,每一项都是true的话,返回true

    2.filter() //将返回true的项组成新的素组

    3.forEach()//仅仅循环数组一次.没有返回值

    4.map()//将每一项的返回值组成新的数组

    5.some()//返回布尔值,其中一项返回true,则返回true

   5.关于函数是否能复制,目前只能想到eval 这种方法了 eval("var z = "+s1.toString());

   6.创建函数有一种新的思路

      var sum = function(num1, num2){

              return num1 + num2;

      }

    等于

继续阅读 »

对象属性的理解

var person = {name:"jie",age:18};
如果只是这样定义一个object,那么configurable:true,Enumerable:true,writable:true;

要改变这些属性类型,需要显式的调用Object.defineProperty
Object.defineProperty(person,"name",{
    configurable:false, //能否被删除,能否修改访问属性,默认true,如果在false重设defineProperty会报错
    Enumerable:false,//能否被for-in,默认为true
    writable:false, //能否修改,默认为true
    value:"gax"
})

console.log(person); //{name: "gax",age:18}
person.name = "abc";
console.log(person); //{name: "gax",age:18}

[configurable]
Object.defineProperty(person,"name",{
    configurable:false,
	value:"jie"
})
delete person.name; //报错
Object.defineProperty(person,"name",{
    value:"jie2"
})//报错
configurable如果被设置false,那么如果delete会报错,如果configurable,Enumerable,writable,value中,任意一项于初始值不一致,都会报错

[Enumerable]
var person = {name:"jie"};
Object.defineProperty(person,"age",{
    Enumerable:false,
	value:"18"
})

for(var x in person){
	console.log(x);
}
//只有name

[writable]
var person = {};
Object.defineProperty(person,"age",{
    writable:false,
    value:"18"
})
person.age = 21;//依然是 18

如果
var person = {};
Object.defineProperty(person,"age",{
    value:"18"
})
那么Object.getOwnPropertyDescriptor(person,"age")//{value: 18, writable: false, enumerable: false, configurable: false}
全部未定义的默认全部false

同理适用于new出来的实例:
function person(){
	Object.defineProperty(this,"age",{
		writable:false,
		value:"18"
	})
}
var son = new person();
这样就能做到对象中的权限控制。