分类目录归档:JavaScript

JavaScript的技术文章

WebPack 傻瓜指南

知乎上的WebPack三连击,非常实用的东西。分享一下。

  • Webpack傻瓜式指南(一)http://zhuanlan.zhihu.com/FrontendMagazine/20367175
  • Webpack傻瓜式指南(二)http://zhuanlan.zhihu.com/FrontendMagazine/20397902
  • Webpack傻瓜式指南(三)http://zhuanlan.zhihu.com/FrontendMagazine/20522487

 

Gulp前端自动化工具

前端日常工作是什么?拿到PSD图,然后开切!首先我们建立一个app的文件夹,然后在app下,我们建立img文件夹,接下来是 css …… 项目终于搞定之后,我们要使用在线压缩工具把样式表进行合并压缩,把所有的大尺寸图片重新导入Ps,保存为 web 格式,然后调整质量 。。。。。。

你就没想过偷懒的办法么?你就是这么把时间耗在没有意义的简单机械的活儿中么?

或者,很多前端都已经开始使用Grunt这样的工具了,自动压缩、自动打包、文件合并、Less,Sass 编译、文件监控、本地文件服务器调试环境。不错,基于Grunt另外推出了非常多的 generate ,类似 angularJS , Ember , Backbone, webapp, bootstrap 等等等等,但是 Grunt 的学习曲线也是非常陡的。虽然能够通过一些通用脚手架来搭建前端工程作为基础架构。一旦需要修改配置,则是异常痛苦。

gulp 的功效和 Grunt 也是异曲同工、插件也是异常丰富。

通过 Grunt 与 gulp 的对比。我个人还是比较喜欢 gulp 的一些操作模式。不管是从词法、编程语义的理解程度来说。gulp 都是优于 Grunt 。官网的介绍谈到了文件流、以及处理速度。因为没有现成的案例,所以不能妄自评论。下面就浅谈一下两者之间的使用细节。

这里引用?http://markgoodyear.com/2014/01/getting-started-with-gulp/ 中的例子:

Grunt

sass: {
  dist: {
    options: {
      style: 'expanded'
    },
    files: {
      'dist/assets/css/main.css': 'src/styles/main.scss',
    }
  }
},

autoprefixer: {
  dist: {
    options: {
      browsers: [
        'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
      ]
    },
    src: 'dist/assets/css/main.css',
    dest: 'dist/assets/css/main.css'
  }
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

Gulp

gulp.task('sass', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(sass({ style: 'compressed' }))
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .pipe(gulp.dest('dist/assets/css'))
});

不管从代码还是从阅读性上来说,Gulp 不是更加优越一些嘛?从代码的输入和输出,以及事件的操作顺序一幕了然,完全不用在复杂的GruntFile中间去找各种各样的变量表达式。妈妈再也不用担心我的学习!

那么我们来一个简单的Case吧。

首先确保你安装了NodeJS。

然后运行 继续阅读

复习Javascript的一些代码(未完待续)

偶尔翻出来以前写的一些小东西。做应用开发久了,久而久之一开始就很注意框架的选择。但是最初写Javascript那种alert逐步调试。写一些通用的工具类的时间仿佛离我很远了。翻了下以前的一些代码,把他贴出来。就权当是复习下吧。

/**
 * author:      可乐加糖
 * description: 呵呵,不知道写什么好,随便吧。
 */

(function (window) {
    //基础类库
    var cola = {
        get_node: function (elem_id) {
            if (elem_id.constructor != String) {
                throw new Error("参数传递错误");
                return false;
            }
            return document.getElementById(elem_id) || null;
        },
        prev: function (elem) {
            do {
                elem = elem.previousElementSibling
            } while (elem && elem.nodeType != 1);
            return elem
        },
        next: function (elem) {
            do {
                elem = elem.nextSibling;
            } while (elem && elem.nodeType != 1);
            return elem
        },
        first_node: function (elem) {
            elem = elem.firstChild;
            return elem && elem.nodeType != 1 ? this.next(elem) : elem;
        },
        last: function (elem) {
            elem = elem.lastChild;
            return elem && elem.nodeType != 1 ? prev(elem) : elem;
        },
        parent: function (elem, num) {
            num = num || 1;
            for (var i = 0; i < num; i++) {
                if (elem != null)
                    elem = elem.parentNode;
                return elem;
            }
        },
        remove: function (elem) {
          if(elem)
            elem.parentNode.removeChild(elem);
        },
        empty: function (elem) {
            while(elem.firstChild)
                elem.removeChild(elem.firstChild)
        }
    }
    window.cola = cola;
})(window)

这是最开始写的一个二级下拉导航条的代码

document.getElementsByClassName = function(clsName){
    if(typeof(clsName)!="string" || clsName.constructor != String){
        throw new Error("className必须是字符串");
        return false;
    }
    var nodes = document.getElementsByTagName("*");
    var nodeArr = new Array();
    for(var i= 0,icount = nodes.length;i<icount;i++){
        if(nodes[i].className == clsName)
            nodeArr.push(nodes[i]);
    }
    return nodeArr;
}
window.onload = function(){
    var nodes = document.getElementsByClassName("has-child");
    for(var i= 0,icount = nodes.length;i<icount;i++){
        nodes[i].onmouseover = function(){
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "block";
        }
        nodes[i].onmouseout = function () {
            var child_menu = this.getElementsByTagName("ul");
            child_menu[0].style.display = "none";
        }
    }
}

还有获取元素的样式表,当初也是在IE下调试了很久才搞定。

function getStyle(elem,name){
    if(elem.style[name])
        return elem.style[name];
    else if(elem.currentStyle)
        return elem.currentStyle[name];
    else if(document.defaultView && document.defaultView.getComputedStyle){
        name = name.replace(/([A-Z])/g,"-$1");
        name = name.toLowerCase();
        var s = document.defaultView.getComputedStyle(elem,"");
        return s && s.getPropertyValue(name);
    }
    else
        return null;
}

关于Sencha ExtJS 中 Datefield 控件 load 无法加载数据的问题

Sencha ExtJS 开发中一个比较多的功能就是在编辑某条数据列的时候如果有 datefield 控件时间无法加载入正确的栏目中。

1

而 textfield 控件则能够通过服务器端返回的数据正确加载到相应的控件中,通过官网的论坛给出的答案是:需要修改服务器端返回的时间格式


{
desc: "成功",
totalResult: 9,
items: [
	{
		userId: "null1383919764373",
		userAccount: "yz",
		userIcon: "",
		userMobile: "13888889991",
		userNick: "园长",
		userType: "3",
		regTime: "2013-11-08 22:09:24",
		updateTime: "2013-11-08 22:09:24",
		status: "0",
		point: 0,
		realName: "园长",
		email: "balkc@11.com",
		sex: "0",
		birthday: "2013-11-06 00:00:00",
		orgs: [{
			updateTime: "2013-11-08 22:09:24",
			status: "0"
		}]
	}
	...
Ext.getCmp('base_form').getForm().on({
        actioncomplete: function(form, action) {
            if (action.type === 'load') {
                if (action.result.success) {
                    //绑定开始时间
                    var date = new Date(action.result.data.regTime);
                    var edate = new Date(action.result.data.updateTime);
                    form.setValues({
                        regTime: Ext.Date.format(date,"Y-m-d"),
                        updateTime: Ext.Date.format(edate,"Y-m-d")
                    });
                }
            }
        }
    });

通过使用 Ext.Date.format(框架内置)方法格式化返回的时间格式就能够正常的把时间设置进入控件中。

Javascript属性constructor/prototype的底层原理(转)

最近一直在阅读 JQuery 的源码,涉及到核心组件,需要复习一下 JavaScript 的底层属性,把这篇文章转载过来,作为资料。
原帖地址:http://blog.csdn.net/hikvision_java_gyh/article/details/8937157

在Javascript语言中,constructor属性是专门为function而设计的,它存在于每一个function的prototype属性中。这个constructor保存了指向function的一个引用。在定义一个函数(代码如下所示)时,


function F() {   
    // some code  
 }

JavaScript内部会执行如下几个动作:

为该函数添加一个原形属性(即prototype对象).

为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的一个引用。

这样当我们把函数F作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数F)的prototype对象的一个属性__proto__,所以我们在每一个对象实例中就可以访问构造函数的prototype所有拥有的全部属性和方法,就好像它们是实例自己的一样。

当然该实例也有一个constructor属性了(从prototype那里获得的),这时候constructor的作用就很明显了,因为在这时,每一个对象实例都可以通过constrcutor对象访问它的构造函数,请看下面代码:

 

[javascript]?view plaincopy


var?f?=?new?F();
alert(f.constructor?===?F);//?output?true
alert(f.constructor?===?F.prototype.constructor);//?output?true

我们可以利用这个特性来完成下面的事情: 对象类型判断,如

[javascript]?view plaincopy


if(f.constructor?===?F)?{
    //?do?sth?with?F
}

其实constructor的出现原本就是用来进行对象类型判断的,但是constructor属性易变,不可信赖。
我们有一种更加安全可靠的判定方法:instanceof 操作符。
下面代码仍然返回true


if(f instanceof F)?{
    // do sth with F
}.

原型链继承,由于constructor存在于prototype对象上,因此我们可以结合constructor沿着原型链找到最原始的构造函数,如下面代码:


function Base() {
}
// Sub1 inherited from Base through prototype chain
function Sub1() {
}
Sub1.prototype = new Base();
Sub1.prototype.constructor = Sub1;
Sub1.superclass = Base.prototype;
// Sub2 inherited from Sub1 through prototype chain
function Sub2() {
}
Sub2.prototype = new Sub1();
Sub2.prototype.constructor = Sub2;
Sub2.superclass = Sub1.prototype;
// Test prototype chain
alert(Sub2.prototype.constructor);
// function Sub2(){}
alert(Sub2.superclass.constructor);
// function Sub1(){}
alert(Sub2.superclass.constructor.superclass.constructor);
// function Base(){}

上面的例子只是为了说明constructor在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承,关于继承我们有好多可以分析和讨论,本篇限于篇幅不在此讨论。
一个容易掉入的陷阱(gotchas) 之前提到constructor易变,那是因为函数的prototype属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:


function F() {

}
F.prototype = {
    _name:Eric,
    getName: function () {
        return this._name;
    }
}

初看这种方式并无问题,但是你会发现下面的代码失效了:

var f = new F();
alert(f.constructor === F); // output false

怎么回事?F不是实例对象f的构造函数了吗?
当然是!只不过构造函数F的原型被开发者重写了,这种方式将原有的prototype对象用一个对象的字面量{}来代替。
而新建的对象{}只是Object的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个constructor属性,因为这是function创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。
然而你会发现constructor并不是不存在的,下面代码可以测试它的存在性:

alert(typeof f.constructor == ‘undefined’);// output false

既然存在,那这个constructor是从哪儿冒出来的呢?

我们要回头分析这个对象字面量{}。

因为{}是创建对象的一种简写,所以{}相当于是new Object()。

那既然{}是Object的实例,自然而然他获得一个指向构造函数Object()的prototype属性的一个引用__proto__,又因为Object.prototype上有一个指向Object本身的constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,下面代码可以验证其结论:

alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true

一个解决办法就是手动恢复他的constructor,下面代码非常好地解决了这个问题:


function F() {
}
F.prototype = {
    constructor: F, /* reset constructor */
    _name: Eric,
    getName: function () {
        return this._name;
    }
};

之后一切恢复正常,constructor重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个constructor?它又是哪儿来的?
细心的朋友会发现,像JavaScript内建的构造函数,如Array, RegExp, String, Number, Object, Function等等居然自己也有一个constructor:
alert(typeof Array.constructor != ‘undefined’);// output true
经过测试发现,此物非彼物它和prototype上constructor不是同一个对象,他们是共存的:
alert(typeof Array.constructor != ‘undefined’);// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为构造函数也是函数。
是函数说明它就是Function构造函数的实例对象,自然他内部也有一个指向Function.prototype的内部引用__proto__啦。
因此我们很容易得出结论,这个constructor(构造函数上的constructor不是prototype上的)其实就是Function构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
OK, constructor从此真相大白,你不在对它陌生了~

Sencha ExtJS 常见问题集合贴

最近一直在使用Sencha进行前端开发,单独开一个帖子,用于常见问题的整理。会陆陆续续往里面添加一些内容,同事也希望能够帮助到大家。

ExtJS submit 提交导致 emptyText 提交进入表单:

win.form.submit({
     submitEmptyText: false,
     url : 'xxxxx',
     method : 'post'
});

ExtJS 限制text最大字符数。

{
	fieldLabel: '教育证号',
	name:"teacher.educationCardNo",
	emptyText:'请输入教育证号',
	enforceMaxLength:true,   //关键是这个
	maxLength:18,
	value:""
}

ExtJS Ajax 提交SESSION失效的问题。

1. 首先通过后端返回 session timeout 的 header 信息,再通过 response.getResponseHeader 来判断是否存在这样的信息来确定session是否失效,代码如下:

Ext.onReady(function(){
	//请求超时的处理开始
	Ext.Ajax.on('requestcomplete', checkUserSessionStatus, this);
	function checkUserSessionStatus(conn, response, options) {
	    var sessionStatus = response.getResponseHeader("sessionstatus");
	    if (typeof(sessionStatus) != "undefined") {
	        Ext.Msg.alert('提示', '会话超时,请重新登录!',
	        function(btn, text) {
	            if (btn == 'ok') {
	                var redirect = root_path;
	                window.location = redirect;
	            }
	        });
	    }
	};
......

Ajax 的提交失败问题。

Ext.Ajax.request 提交与 Ext.Form.panel 中的 submit 都是属于 Ajax 提交,但是前者判断返回不是由服务器端的 { success :false …} 来决定,而是通过返回的 responseText 中的返回值来判断, from 中的提交,可通过?failure 函数来判定。代码如下:

Ajax 返回失败:

Ext.Ajax.request({
    url: url,
    timeout: 6000,
    success: function(response) {
        var result = Ext.decode(response.responseText);
        if (result.success) {
            Ext.Msg.alert("提示", "数据删除成功");
        } else {
            Ext.Msg.alert("提示", result.message);
        }
        parentStore.reload();
    },
    error: function(response) {
        Ext.Msg.alert("提示", "对不起,操作失败。");
        parentStore.reload();
    }
});

form 表单提交失败:

Ext.getCmp("wx_student_add").submit({
    submitEmptyText: false,
    clientValidation: true,
    url: this.url,
    method: 'post',
    success: function(form, action) {
        top.Ext.Msg.alert("提示", "保存成功");
        Ext.getCmp("tw_addstudent").destroy();
        student_store.reload();
    },
    failure: function(form, action) {
        Ext.Msg.alert("提示", "数据提交失败");
        throw new Error("用户数据保存失败,请检查服务器端返回");
    }
});

待续……

ExtJs 自定义组件事件绑定

优化了一下代码,结果如下。

调用:

<script type="text/javascript">
function aaa(){
	var student_panel = new WX.student.AddStudent({
		callback_fun:function(){
			alert("我是页面的方法");
		}
	}).show();
}
</script>

组件定义

Ext.define('WX.student.AddStudent', {
	
    extend: 'Ext.window.Window',

    modal:true,
    height: 585,
    width: 590,
    layout: {
        type: 'fit'
    },
    title: '新增学生',
    //用于组件函数回调
    callback_fun:function(){},
    initComponent: function() {
    	
        var me = this;
        
        Ext.applyIf(me, {
            items: [
                {
                    waitTitle: '加载中...',
                    items: [
                        ......
                    ],
                    buttons:[{
                    	text:'保存',
                    	scope:this,                  // <============== 关键参数 
                    	handler:function(){
                    		//DO SOMETHING...
                    		alert("组件的事件");
                    		this.callback_fun();
                    	}
                    }]
                }
            ]
        });
        me.callParent(arguments);
    }

});

Sencha 自定义组件函数回调

工程里用到了Sencha ExtJs,因为特定的组件原因,导致很多组件得自己。组件定义完成之后的 Success_callback 因为之前没做过,所以这次把它记录下来方便以后查阅。直接上代码:

界面调用

<script type="text/javascript">
function aaa(){
	var student_panel = new WX.student.AddStudent({
		//传入回调函数,直接显示。
		callback_fun:function(){
			alert('我是界面的回调函数');
		}
	}).show();
}
</script>

组件部分代码

Ext.define('WX.student.AddStudent', {

    extend: 'Ext.window.Window',

    modal:true,
    height: 585,
    width: 684,
    layout: {
        type: 'fit'
    },
    title: '新增学生',
    bodyPadding:'10 10 10 10',
    //用于组件函数回调
    callback_fun:function(){},
    initComponent: function() {
        var me = this;
        ......
        // Panel 组件
        buttons:[{
                text:'保存',
                handler:function(){
                     //发送Ajax请求保存用户数据.

                    //调用STORE之后的自定义回调函数
                    this.callback_fun
                }
        }]
    }
.....

这里的做法有点类似Java的接口回调,在自定义模块中定义方法,提供按钮回调,界面初始化组件的时候用方法覆盖组件中的回调方法,达到回调的目的。

tabs复习(jQuery)

已经不记得是多久没有写过了,最近的状态还不错,因为各种考试的缘故,很多技术文章都是看了,没有往上面写。在亚马逊购了一些H5,CSS3以及移动互联网开发的书,等驾照考过之后就又要开启学习模式了。

今天闲来无聊,也就把jQuery里面的几个点复习了一边,写了一个小DEMO。复习点是jQuery的自定义事件。

直接帖代码上来了。

jQuery.fn.tabs = function (control) {
    var element = $(this);
    control = $(control);
    //初始化完毕

    element.delegate("li", "click", function () {
        //找到需要修改的目标
        var tabName = $(this).attr("data-tab");
        //点击选项卡触发自定义事件
        element.trigger("change.tabs", tabName);
    });

    element.bind("change.tabs", function (e, tabname) {
        element.find("li").removeClass("active");
        element.find(">[data-tab='" + tabname + "']").addClass("active");
    });

    element.bind("change.tabs", function (e, tabname) {
        control.find(">[data-tab]").removeClass("active");
        control.find(">[data-tab='" + tabname + "']").addClass("active");
    });

    var firstName = element.find("li:first").attr("data-tab");
    element.trigger("change.tabs",firstName);

    return this;
}

自定义事件在日常的WEB开发中是非常常见的功能,用于封装自有组件定义自有API。实在是居家旅行,开发面试必备技能。

JavaScript错误与调试

前端开发中,应该除非大型团队对编程有着严格的日志控制和错误机制,在小型团队中的前端使用错误与调试还是基本上靠Firebug等调试工具。前端迭代时间短周期也比较短,在这方面也的确是一个欠缺。

阅读了《编写可维护的JavaScript》书中的第十章 ”抛出自定义错误“ 对代码的书写和规范还是有一定的启发。把一些有用的东西作为笔记记下来。

错误类型:

  • Error – 基本错误类型
  • EvalError – 通过 eval() 函数执行代码发生的错误抛出
  • RangeError – 数字超出其边界时抛出(例:创建一个负数索引的数组)
  • ReferenceError – 期望的对象不存在
  • SyntaxError – 给 eval() 函数传递的代码中有语法错误
  • TypeError – 变量不是期望的类型(new 10,’prop’ in true)
  • URLError – 常见于 encodeURI()、encodeURIComponent(),decodeURI() 等传递了非法格式的URI字符串时抛出。

关于 Try-Catch 的应用

  • 修复了一个难以调试和修复的BUG之后,尝试增加自定义错误,以便于下次调试时快速的定位错误代码。
  • 编写代码时的一些防御型、预见性的错误可以尝试添加错误处理函数。
  • 正在编写的代码需要提供给其他人使用,如果按照对方的思维,在特定的情况中抛出错误处理

错误以及错误处理不是为了防止错误,而是为了在错误发生时能够更加快速的调试和定位错误。