js集锦(下)

此部分介绍了。。。

javaScript中的装箱和拆箱 http://www.108js.com/article/article1/10034.html?id=175
javaScript中的Object.valueOf()方法 http://www.108js.com/article/article1/10108.html?id=732
我的问题依然是(+Var)到底代表什么意思?

变量前加上+后,变量将转换为数字,进行数字运算。比如:

var x = "3";
var y = x + 10; // 310
var z = +x + 10 // 13

赋予x值为3,y=x + 3,运算结果为310,其中的+号为连接作用,这不难理解。z=+x + 10,运算结果为13,这是将+x的值3与后方的10做数字运算,所以结果13,这就是+var的用意。

Javascript面向对象编程(三):非构造函数的继承

这个系列的第一部分介绍了”封装”,第二部分介绍了使用构造函数实现”继承”。
今天是最后一个部分,介绍不使用构造函数实现”继承”。
一、什么是”非构造函数”的继承?
比如,现在有一个对象,叫做”中国人”。
  var Chinese = {
  nation:’中国’
};
还有一个对象,叫做”医生”。
var Doctor ={
career:’医生’
}
请问怎样才能让”医生”去继承”中国人”,也就是说,我怎样才能生成一个”中国医生”的对象?
这里要注意,这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现”继承”。
二、object()方法
json格式的发明人Douglas Crockford,提出了一个object()函数,可以做到这一点。
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
这个object()函数,其实只做一件事,就是把子对象的prototype属性,指向父对象,从而使得子对象与父对象连在一起。
使用的时候,第一步先在父对象的基础上,生成子对象:
var Doctor = object(Chinese);
然后,再加上子对象本身的属性:
Doctor.career = ‘医生’;
这时,子对象已经继承了父对象的属性了。
alert(Doctor.nation); //中国
三、浅拷贝
除了使用”prototype链”以外,还有另一种思路:把父对象的属性,全部拷贝给子对象,也能实现继承。
下面这个函数,就是在做拷贝:
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
使用的时候,这样写:
var Doctor = extendCopy(Chinese);
Doctor.career = ‘医生’;
alert(Doctor.nation); // 中国
但是,这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
请看,现在给Chinese添加一个”出生地”属性,它的值是一个数组。
Chinese.birthPlaces = [‘北京’,’上海’,’香港’];
通过extendCopy()函数,Doctor继承了Chinese。
var Doctor = extendCopy(Chinese);
然后,我们为Doctor的”出生地”添加一个城市:
Doctor.birthPlaces.push(‘厦门’);
发生了什么事?Chinese的”出生地”也被改掉了!
alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港, 厦门
所以,extendCopy()只是拷贝基本类型的数据,我们把这种拷贝叫做”浅拷贝”。这是早期jQuery实现继承的方式。
四、深拷贝
所谓”深拷贝”,就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难,只要递归调用”浅拷贝”就行了。
function deepCopy(p, c){
var c = c || {};
for (var i in p){
if (typeof p[i] === ‘object’){
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
}else{
c[i] = p[i];
   }
}
return c;
}
使用的时候这样写:
var Doctor = deepCopy(Chinese);
现在,给父对象加一个属性,值为数组。然后,在子对象上修改这个属性:
  Chinese.birthPlaces = [‘北京’,’上海’,’香港’];
Doctor.birthPlaces.push(‘厦门’);
这时,父对象就不会受到影响了。
alert(Doctor.birthPlaces); //北京, 上海, 香港, 厦门
alert(Chinese.birthPlaces); //北京, 上海, 香港
目前,jQuery库使用的就是这种继承方法。

prototype.js中创建类的方式

各种库的写类方式虽然千奇百怪,但仍然逃离不了本质—用构造函数和原型来组装类。
一、prototype.js中的代码
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
//简化后的
function Clazz() {
return function(){
this.initialize.apply(this,arguments);
}
}
Class用来创建一种类型,注意其属性create是一个方法,返回一个构造函数。 一般使用如下:
var X = Class.create();
返回一个类型,类似于java的一个Class实例。要使用X类型,需继续用new X()来获取一个实例,如同java的Class.newInstance()方法。 返回的构造函数会执行名为initialize的方法,此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。
Class.create实际上是返回一个函数,那么new的时候,做了些什么呢。new的时候会执行该返回的函数,即执行
this.initialize.apply(this, arguments);
此时的this就是新生成的对象,这也就是说了所有对象的初始化工作全部委托给initialize函数了。
二、怎样写一个类
//类名Person
//这里得到的是Person的initialize调用, 所以是Person.initialize(arguments)
//相当于构造函数persion(name){ this.name=name }
var Person = Class.create();
//通过原型重写来定义Person
Person.prototype = {
initialize : function(name) {
this.name = name;
},
getName : function() {
return this.name;
},
setName : function(name) {
this.name = name;
}
}
//创建对象
var p = new Person(“jack”);
alert(p.getName());
alert(p.constructor == Person);//false
initialize完成对象的初始化(相当于构造函数),方法依次往下写即可。 有个问题,通过这句p.constructor == Person为false可以看到,这正是Prototype.js一个小小的缺陷。原因是重写了Person的原型。为了使constructor能指向正确的构造器,只需在原型重写时维护好constructor属性即可。
Person.prototype = {
constructor : Person,//注意这里
initialize : function(name) {
this.name = name;
},
getName : function() {
return this.name;
},
setName : function(name) {
this.name = name;
}
}
好了,这时候p.constructor == Person就是true了。
Js函数声明与函数表达式之区别
在定义函数时,我们一般使用下面这两种方法:
使用函数声明定义:
function sum (a, b) {
return a + b;
}
使用函数表达式定义:
var sum = function (a, b) {
return a + b;
}
调用方法都是一样的:如求“1+1”等于几:
alert(sum(1, 1));
但这两种方法还是有区别的。解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。解析器会率先读取函数声明,并使其在执行任何代码之前可用;而函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
示例:
alert(sum(1, 1));
function sum (a, b) {
return a + b;
}
以上代码可以正常执行。因为在代码执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。在对代码求值时,Javascript引擎在第一遍会声明函数并将它们放到源代码树的顶部。所以,即使声明函数的代码放到了调用它的代码的后面,Javascript 引擎也能把函数声明提升到顶部。如果像下面例子所示,把上面的函数声明改为函数表达式,就会在执行期间导致错误。

alert(sum(1, 1));
var sum = function (a, b) {
return a + b;
}
以上代码之所以会在执行期间产生错误,原因在于函数位于一个初始化语句中,而不是一个函数声明。换句话说,在执行到函数所在的语句之前,变量sum不会保存有对函数的引用,并且第一行代码已经产生错误,也就不会执行到下一行。
总体来说,除了什么时候可以通过变量访问函数这一点区别之外,函数声明与函数表达式的语法其实是等价的。
通常我们会看到以下两种定义函数的方式:
// 函数语句
function fn(str)
{
console.log(str);
};

// 表达式定义
var fnx=function(str)
{
console.log(str+ ‘ from fnx’);
};

以前都是凭借自己手指的感觉随心所欲使用两者 -_- || ,今天看了js基础,总算是解决了心中对他们的困惑:
两种方式都创建了新的函数对象, 但函数声明语句的函数名是一个变量名,变量指向函数对象,和通过var声明变量一样,函数定义语句中的函数被显示地提前到了脚本或函数的顶部,因此它们在整个脚本和函数内都是可见的,但是使用var表达式定义函数,只有变量声明提前了,变量初始化代码仍然在原来的位置,用函数语句创建的函数,函数名称和函数体均被提前,所以我们可以在声明它之前就使用它。
代码例子如下:
console.log(typeof(fn)); // function
fn(‘abc’); // abc

console.log(typeof(fnx)); // undefined

if(fnx)
fnx(‘abc’); // will not execute
else
console.log(‘fnx is undefined’); // fnx is undefined

// 函数语句
function fn(str)
{
console.log(str);
};

// 表达式定义
var fnx=function(str)
{
console.log(str+ ‘ from fnx’);
};
代码很简单, 希望和我之前一样没有弄明白两者区别的同学能有所收获 。
回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

javaScript中克隆对象的三种方法

方法1 (点击查看结果)
function clone(obj){
var o;
if(typeof obj == “object”){
if(obj === null){
o = null;
}else{
if(obj instanceof Array){
o = [];
for(var i = 0, len = obj.length; i < len; i++){
o.push(clone(obj[i]));
}
}else{
o = {};
for(var k in obj){
o[k] = clone(obj[k]);
}
}
}
}else{
o = obj;
}
return o;
}

//测试
var parent = {
data: {
name: ‘jack’,
age: 30,
isMarried: false
},
language: [‘Java’],
show:function(){ return “ok” ;}
}
var Cparent=clone(parent);
alert(Cparent.data.age);
alert(Cparent.language[0]);
alert(Cparent.show());
方法2:(点击查看结果)
function clone(obj){
var o, obj;
if (obj.constructor == Object){
o = new obj.constructor();
}else{
o = new obj.constructor(obj.valueOf());
}
for(var key in obj){
if ( o[key] != obj[key] ){
if ( typeof(obj[key]) == ‘object’ ){
o[key] = clone(obj[key]);
}else{
o[key] = obj[key];
}
}
}
o.toString = obj.toString;
o.valueOf = obj.valueOf;
return o;
}
//测试
var parent = {
data: {
name: ‘jack’,
age: 30,
isMarried: false
},
language: [‘Java’],
show:function(){ return “ok” ;}
}
var Cparent=clone(parent);
alert(Cparent.data.age);
alert(Cparent.language[0]);
alert(Cparent.show());
方法3 (点击查看结果)
function clone(obj){
function Fn(){}
Fn.prototype = obj;
var o = new Fn();
for(var a in o){
if(typeof o[a] == “object”) {
o[a] = clone(o[a]);
}
}
return o;
}
//测试
var parent = {
data: {
name: ‘jack’,
age: 30,
isMarried: false
},
language: [‘Java’],
show:function(){ return “ok” ;}
}
var Cparent=clone(parent);
alert(Cparent.data.age);
alert(Cparent.language[0]);
alert(Cparent.show());

JavaScript原型继承之陷阱 http://www.108js.com/article/article1/10130.html?id=766

javascript中的function与感叹号 其实无论是括号,还是感叹号,让整个语句合法做的事情只有一件,就是让一个函数声明语句变成了一个表达式。
javascript中双感叹号的使用
js中的双感叹号判断。他相当于三元运算符,返回boolean值。
今天发现一段很奇怪的代码,虽然能领会他的意思,但是不明白双感叹号起到的作用。 代码如下:
function id( name ) {
return !!( typeof document !== “undefined” && document && document.getElementById ) &&
document.getElementById( name );
}
然后去网上查了些资料,他相当于三元运算符,返回boolean值。
var ret = !!document.getElementById
等价于:
var ret = document.getElementById ? true : false; 
当值是非空字符串和非零数字返回true,当值是空字符串、0或者null返回false。
代码如下:

var a = " "; alert(!!a);//true
var a = "s"; alert(!!a);//true
var a = true; alert(!!a);//true
var a = 1; alert(!!a);//true
var a = -1; alert(!!a);//true
var a = -2; alert(!!a);//true

var a = 0; alert(!!a);//false
var a = ""; alert(!!a);//false
var a = false; alert(!!a);//false
var a = null; alert(!!a);//false

JS笔试题

考察this (点击查看结果)
 var length = 10
 function fn(){
   alert(this.length)
 }
 var obj = {
  length: 5,
  method: function(fn) {
  fn() // ?
  arguments[0]() // ?
 }
}
obj.method(fn)

这里的坑主要是arguments,我们知道取对象属于除了点操作符还可以用中括号,这里fn的scope是arguments,即fn内的this===arguments,调用时仅传了一个参数fn,因此length为1。

函数表达式具名(函数声明同时赋值给另一个变量)或函数声明立即执行时,名仅在该函数内可访问

~function() {
alert(typeof next) // ?
~function next() {
alert(typeof next) // ?
}()
}()
外层匿名函数自执行,打印next,接着内层具名函数自执行。会发现具名的next仅在其自身函数体内可访问,即输出为function。外面是不可见的,typeof就为undefined了。(注:此题IE6/7/8中输出为function function, 标准浏览器为undefined function)
同样的情况也发生在将具名函数赋值给一个变量时,如下
var func = function a() {
alert(typeof a)
}
func() // ?
alert(typeof a) // ?
这条规则是标准中(ES3/ES5)都已明确指出,但IE6、7、8没有严格遵从。可参见w3help的分析或李松峰老师的翻译《命名函数表达式探秘》

给基本类型数据添加属性,不报错,但取值时是undefined

a = 3
a.prop = 4
alert(a + a.prop) // ?
变量a为数字3,给其添加prop属性,值为4(奇怪吧在JS中这是允许的,且不会有语法错误)。然后alert出a+a.prop的结果。结果是NaN。a.prop为undefined,3+undefined为NAN。
举一反三,给字符串添加属性
str = ‘a’
str.prop = ‘b’
alert(str + str.prop) // ?
结果呢?

隐式的全局变量

var a = 1
function func() {
a = b = 2
}
func()
alert(a)
alert(b) // ?
JS中不用var声明的变量默认是全局变量,而这里的连等使的情况更加隐蔽。这里的b是全局的,因此func外可以访问。

变量声明早于代码运行(Scoping and Hoisting)

var uname = ‘jack’
function change() {
alert(uname) // ?
var uname = ‘lily’
alert(uname)
}
change()
这里容易犯迷糊的是第一个alert,如果认为函数change外已经声明赋值了,此时应该是jack,实际函数内又var了一次(虽然var在其后),预解析时仍然会将其置undefined。这也是为什么书里都建议变量声明都放在代码块的最前面。

函数声明早于变量声明

function change() {
alert(typeof fn) // ?
function fn() {
alert(‘hello’)
}
var fn
}
change();
change内先alert出fn,后函数声明,再变量声明。如果fn没有函数声明而仅是变量声明,那么结果与5一样是undefined。但这里却是function。即同一个作用域内,函数声明放在代码块后面和前面都没有关系,函数可以正常使用。而变量声明则需先置前,先使用则是undefined。

变态题

http://www.108js.com/article/article1/10112.html?id=738
http://www.108js.com/article/article1/10113.html?id=739
http://www.108js.com/article/article1/10110.html?id=739
http://www.108js.com/article/article1/10167.html?id=885

js之Genreator

Generator基础相关知识
Generator是ECMAScript6(代号harmory)中提供的新特性
1、Generator的定义
和普通函数相比,只是在function关键字后加个*号
Generator对象具有一个next方法,调用一次next方法才能执行定义的逻辑代码
2、yield关键字
yield关键字让Generator内部的逻辑能够切割成多个部分
A、切割函数逻辑
将一个函数的逻辑拆分为n(n=该函数中yield关键字数目)个,且它们共享上下文
B、yield关键字对next()方法的影响
每次.next()调用时,返回一个对象,这个对象具备两个属性,其中一个属性是表示这个Generator对象的逻辑块是否执行完成的布尔类型的done,另一个是yield语句后的表达式的结果value。若value属性值为一个函数,能直接通过value属性调用该函数
C、yield暂停Generator逻辑的理解
yield只能暂停Generator内部的逻辑,它并不是真正暂停整个线程,Generator外的业务逻辑依然会继续执行下去
3、next()方法
A、next()方法对yield关键字的影响
可以通过.next()传递参数,赋值给yield关键字前面的变量声明
PS:对于Generator而言,它不仅可以将逻辑的单元切分得更细外,还能在暂停和继续执行的间隔中,动态传入数据,使得代码逻辑可以更灵活

很惭愧<br><br>只做了一点微小的工作<br>谢谢大家